diff --git a/.eslintrc.json b/.eslintrc.json index 07b395312c..40cd9251a6 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -212,6 +212,7 @@ "restrictions": [ "vs/nls", "azdata", + "mssql", "azurecore", "**/{vs,sql}/base/common/**", "**/{vs,sql}/base/parts/*/common/**", @@ -473,6 +474,7 @@ "restrictions": [ "vscode", "azdata", + "mssql", "azurecore", "vs/nls", "**/{vs,sql}/base/common/**", @@ -579,6 +581,7 @@ "vs/nls", "vs/css!./**/*", "azdata", + "mssql", "azurecore", "vscode", "**/{vs,sql}/base/**/{common,browser,worker}/**", diff --git a/extensions/mssql/config.json b/extensions/mssql/config.json index 63a535b701..0fac624626 100644 --- a/extensions/mssql/config.json +++ b/extensions/mssql/config.json @@ -1,6 +1,6 @@ { "downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}", - "version": "3.0.0-release.234", + "version": "3.0.0-release.237", "downloadFileNames": { "Windows_86": "win-x86-net6.0.zip", "Windows_64": "win-x64-net6.0.zip", diff --git a/extensions/mssql/src/azureBlob/azureBlobService.ts b/extensions/mssql/src/azureBlob/azureBlobService.ts new file mode 100644 index 0000000000..2a605d436e --- /dev/null +++ b/extensions/mssql/src/azureBlob/azureBlobService.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type * as mssql from 'mssql'; +import { SqlOpsDataClient } from 'dataprotocol-client'; +import * as contracts from '../contracts'; + +export class AzureBlobService implements mssql.IAzureBlobService { + + public constructor(protected readonly client: SqlOpsDataClient) { } + + public async createSas(ownerUri: string, blobContainerUri: string, blobContainerKey: string, storageAccountName: string, expirationDate: string): Promise { + // This isn't registered as a feature since it's not something that we expect every tools client to implement currently since the usage is + // specifically for ADS and SqlToolsService. + const params: contracts.CreateSasParams = { ownerUri, blobContainerUri, blobContainerKey, storageAccountName, expirationDate }; + return this.client.sendRequest(contracts.CreateSasRequest.type, params).then( + undefined, + e => { + this.client.logFailedRequest(contracts.CreateSasRequest.type, e); + return Promise.resolve(undefined); + } + ); + } +} diff --git a/extensions/mssql/src/constants.ts b/extensions/mssql/src/constants.ts index f415329b73..e36110dab3 100644 --- a/extensions/mssql/src/constants.ts +++ b/extensions/mssql/src/constants.ts @@ -43,6 +43,7 @@ export const objectExplorerPrefix: string = 'objectexplorer://'; export const SqlAssessmentService = 'sqlAssessmentService'; export const SqlMigrationService = 'sqlMigrationService'; export const NotebookConvertService = 'notebookConvertService'; +export const AzureBlobService = 'azureBlobService'; export enum BuiltInCommands { SetContext = 'setContext' diff --git a/extensions/mssql/src/contracts.ts b/extensions/mssql/src/contracts.ts index 4603ca0a8b..87861f1e1f 100644 --- a/extensions/mssql/src/contracts.ts +++ b/extensions/mssql/src/contracts.ts @@ -1111,6 +1111,20 @@ export namespace DisposeTableDesignerRequest { } // ------------------------------- < Table Designer > ------------------------------------ +// ------------------------------- < Azure Blob > ------------------------------------ +export interface CreateSasParams { + ownerUri: string; + blobContainerUri: string; + blobContainerKey: string; + storageAccountName: string; + expirationDate: string; +} + +export namespace CreateSasRequest { + export const type = new RequestType('blob/createSas'); +} + +// ------------------------------- < Azure Blob > ------------------------------------ // ------------------------------- < Execution Plan > ------------------------------------ diff --git a/extensions/mssql/src/mssql.d.ts b/extensions/mssql/src/mssql.d.ts index 759926a5d4..817c00df1c 100644 --- a/extensions/mssql/src/mssql.d.ts +++ b/extensions/mssql/src/mssql.d.ts @@ -48,6 +48,8 @@ declare module 'mssql' { readonly sqlAssessment: ISqlAssessmentService; readonly sqlMigration: ISqlMigrationService; + + readonly azureBlob: IAzureBlobService; } /** @@ -901,4 +903,21 @@ declare module 'mssql' { export interface ISqlMigrationService { getAssessments(ownerUri: string, databases: string[]): Promise; } + + export interface CreateSasResponse { + sharedAccessSignature: string; + } + + export interface IAzureBlobService { + /** + * Create a shared access signature for the specified blob container URI and saves it to the server specified with the connectionUri + * @param connectionUri The connection URI of the server to save the SAS to + * @param blobContainerUri The blob container URI to create the SAS for + * @param blobStorageKey The key used to access the storage account + * @param storageAccountName The name of the storage account the SAS will be created for + * @param expirationDate The expiration date of the SAS + * @returns A created shared access signature token + */ + createSas(connectionUri: string, blobContainerUri: string, blobStorageKey: string, storageAccountName: string, expirationDate: string): Promise; + } } diff --git a/extensions/mssql/src/mssqlApiFactory.ts b/extensions/mssql/src/mssqlApiFactory.ts index f251d71386..43c929e3d6 100644 --- a/extensions/mssql/src/mssqlApiFactory.ts +++ b/extensions/mssql/src/mssqlApiFactory.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { AppContext } from './appContext'; -import { IExtension, ICmsService, IDacFxService, ISchemaCompareService, MssqlObjectExplorerBrowser, ILanguageExtensionService, ISqlAssessmentService, ISqlMigrationService } from 'mssql'; +import { IExtension, ICmsService, IDacFxService, ISchemaCompareService, MssqlObjectExplorerBrowser, ILanguageExtensionService, ISqlAssessmentService, ISqlMigrationService, IAzureBlobService } from 'mssql'; import * as constants from './constants'; import { MssqlObjectExplorerNodeProvider } from './objectExplorerNodeProvider/objectExplorerNodeProvider'; import * as azdata from 'azdata'; @@ -40,6 +40,9 @@ export function createMssqlApi(context: AppContext, sqlToolsServer: SqlToolsServ }, get sqlMigration() { return context.getService(constants.SqlMigrationService); + }, + get azureBlob() { + return context.getService(constants.AzureBlobService); } }; } diff --git a/extensions/mssql/src/sqlToolsServer.ts b/extensions/mssql/src/sqlToolsServer.ts index 35d65c9a05..2f115a89d7 100644 --- a/extensions/mssql/src/sqlToolsServer.ts +++ b/extensions/mssql/src/sqlToolsServer.ts @@ -26,6 +26,7 @@ import { SqlAssessmentService } from './sqlAssessment/sqlAssessmentService'; import { NotebookConvertService } from './notebookConvert/notebookConvertService'; import { SqlMigrationService } from './sqlMigration/sqlMigrationService'; import { SqlCredentialService } from './credentialstore/sqlCredentialService'; +import { AzureBlobService } from './azureBlob/azureBlobService'; const localize = nls.loadMessageBundle(); const outputChannel = vscode.window.createOutputChannel(Constants.serviceName); @@ -91,6 +92,7 @@ export class SqlToolsServer { const resourceProvider = new AzureResourceProvider(context.extensionContext.logPath, this.config); this.disposables.push(credsStore); this.disposables.push(resourceProvider); + context.registerService(Constants.AzureBlobService, new AzureBlobService(this.client)); return Promise.all([credsStore.start(), resourceProvider.start()]).then(); } diff --git a/extensions/sql-database-projects/src/test/testContext.ts b/extensions/sql-database-projects/src/test/testContext.ts index eb03f392f4..de840a5f73 100644 --- a/extensions/sql-database-projects/src/test/testContext.ts +++ b/extensions/sql-database-projects/src/test/testContext.ts @@ -9,7 +9,6 @@ import * as path from 'path'; import * as TypeMoq from 'typemoq'; import * as mssql from 'mssql'; - export interface TestContext { context: vscode.ExtensionContext; dacFxService: TypeMoq.IMock; diff --git a/src/sql/platform/azureBlob/common/azureBlobService.ts b/src/sql/platform/azureBlob/common/azureBlobService.ts new file mode 100644 index 0000000000..dc0cb5a63a --- /dev/null +++ b/src/sql/platform/azureBlob/common/azureBlobService.ts @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * 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 type * as mssql from 'mssql'; + +export const SERVICE_ID = 'azureBlobService'; + +export const IAzureBlobService = createDecorator(SERVICE_ID); + +export interface IAzureBlobService { + _serviceBrand: undefined; + /** + * Create a shared access signature for the specified blob container URI and saves it to the server specified with the connectionUri + * @param connectionUri The connection URI of the server to save the SAS to + * @param blobContainerUri The blob container URI to create the SAS for + * @param blobStorageKey The key used to access the storage account + * @param storageAccountName The name of the storage account the SAS will be created for + * @param expirationDate The expiration date of the SAS + * @returns A created shared access signature token + */ + createSas(connectionUri: string, blobContainerUri: string, blobStorageKey: string, storageAccountName: string, expirationDate: string): Promise; +} diff --git a/src/sql/workbench/api/browser/extensionHost.contribution.ts b/src/sql/workbench/api/browser/extensionHost.contribution.ts index 1ebb9e593a..94cfe6b7d0 100644 --- a/src/sql/workbench/api/browser/extensionHost.contribution.ts +++ b/src/sql/workbench/api/browser/extensionHost.contribution.ts @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - +import './mainThreadAzureBlob'; import './mainThreadAzureAccount'; import './mainThreadAccountManagement'; import './mainThreadBackgroundTaskManagement'; diff --git a/src/sql/workbench/api/browser/mainThreadAzureBlob.ts b/src/sql/workbench/api/browser/mainThreadAzureBlob.ts new file mode 100644 index 0000000000..837c53ceca --- /dev/null +++ b/src/sql/workbench/api/browser/mainThreadAzureBlob.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type * as mssql from 'mssql'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { + ExtHostAzureBlobShape, + MainThreadAzureBlobShape, + SqlExtHostContext, + SqlMainContext +} from 'sql/workbench/api/common/sqlExtHost.protocol'; +import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; +import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; +import { IAzureBlobService } from 'sql/platform/azureBlob/common/azureBlobService'; +import { AzureBlobService } from 'sql/workbench/services/azureBlob/browser/azureBlobService'; + +@extHostNamedCustomer(SqlMainContext.MainThreadAzureBlob) +export class MainThreadAzureBlob extends Disposable implements MainThreadAzureBlobShape { + private _proxy: ExtHostAzureBlobShape; + public _serviceBrand: undefined; + + constructor( + extHostContext: IExtHostContext, + @IAzureBlobService azureBlobService: IAzureBlobService + ) { + super(); + this._proxy = extHostContext.getProxy(SqlExtHostContext.ExtHostAzureBlob); + (azureBlobService as AzureBlobService).registerProxy(this); + } + + public createSas(connectionUri: string, blobContainerUri: string, blobStorageKey: string, storageAccountName: string, expirationDate: string): Thenable { + return this._proxy.$createSas(connectionUri, blobContainerUri, blobStorageKey, storageAccountName, expirationDate); + } +} diff --git a/src/sql/workbench/api/common/extHostAzureBlob.ts b/src/sql/workbench/api/common/extHostAzureBlob.ts new file mode 100644 index 0000000000..ec4f40bd9a --- /dev/null +++ b/src/sql/workbench/api/common/extHostAzureBlob.ts @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as mssql from 'mssql'; +import { ExtHostAzureBlobShape } from 'sql/workbench/api/common/sqlExtHost.protocol'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; + +export class ExtHostAzureBlob extends ExtHostAzureBlobShape { + constructor(@IExtHostExtensionService private _extHostExtensionService: IExtHostExtensionService,) { + super(); + } + + public override $createSas(connectionUri: string, blobContainerUri: string, blobStorageKey: string, storageAccountName: string, expirationDate: string): Thenable { + const api = this.getApi(); + return api.azureBlob.createSas(connectionUri, blobContainerUri, blobStorageKey, storageAccountName, expirationDate); + } + + private getApi(): mssql.IExtension { + return this._extHostExtensionService.getExtensionExports(new ExtensionIdentifier(mssql.extension.name)) as mssql.IExtension; + } +} diff --git a/src/sql/workbench/api/common/sqlExtHost.api.impl.ts b/src/sql/workbench/api/common/sqlExtHost.api.impl.ts index 28f112d36f..2abdea8532 100644 --- a/src/sql/workbench/api/common/sqlExtHost.api.impl.ts +++ b/src/sql/workbench/api/common/sqlExtHost.api.impl.ts @@ -37,6 +37,7 @@ import { ExtHostWorkspace } from 'sql/workbench/api/common/extHostWorkspace'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { URI } from 'vs/base/common/uri'; import { ITelemetryEventProperties } from 'sql/platform/telemetry/common/telemetry'; +import { ExtHostAzureBlob } from 'sql/workbench/api/common/extHostAzureBlob'; import { ExtHostAzureAccount } from 'sql/workbench/api/common/extHostAzureAccount'; import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; @@ -82,6 +83,7 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp rpcProtocol.set(SqlExtHostContext.ExtHostAzureAccount, new ExtHostAzureAccount(accessor.get(IExtHostExtensionService))); const extHostConnectionManagement = rpcProtocol.set(SqlExtHostContext.ExtHostConnectionManagement, new ExtHostConnectionManagement(rpcProtocol)); const extHostCredentialManagement = rpcProtocol.set(SqlExtHostContext.ExtHostCredentialManagement, new ExtHostCredentialManagement(rpcProtocol)); + rpcProtocol.set(SqlExtHostContext.ExtHostAzureBlob, new ExtHostAzureBlob(accessor.get(IExtHostExtensionService))); const extHostDataProvider = rpcProtocol.set(SqlExtHostContext.ExtHostDataProtocol, new ExtHostDataProtocol(rpcProtocol, uriTransformer)); const extHostObjectExplorer = rpcProtocol.set(SqlExtHostContext.ExtHostObjectExplorer, new ExtHostObjectExplorer(rpcProtocol, commands)); const extHostResourceProvider = rpcProtocol.set(SqlExtHostContext.ExtHostResourceProvider, new ExtHostResourceProvider(rpcProtocol)); diff --git a/src/sql/workbench/api/common/sqlExtHost.protocol.ts b/src/sql/workbench/api/common/sqlExtHost.protocol.ts index 15dd9a0480..88385fd26f 100644 --- a/src/sql/workbench/api/common/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/common/sqlExtHost.protocol.ts @@ -14,6 +14,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import type * as azdata from 'azdata'; import type * as vscode from 'vscode'; import type * as azurecore from 'azurecore'; +import type * as mssql from 'mssql'; import { ITreeComponentItem } from 'sql/workbench/common/views'; import { ITaskHandlerDescription } from 'sql/workbench/services/tasks/common/tasks'; @@ -31,6 +32,9 @@ import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import { TreeDataTransferDTO } from 'vs/workbench/api/common/shared/treeDataTransfer'; import { ITelemetryEventProperties } from 'sql/platform/telemetry/common/telemetry'; +export abstract class ExtHostAzureBlobShape { + public $createSas(connectionUri: string, blobContainerUri: string, blobStorageKey: string, storageAccountName: string, expirationDate: string): Thenable { throw ni(); } +} export abstract class ExtHostAzureAccountShape { public $getSubscriptions(account: azurecore.AzureAccount, ignoreErrors?: boolean, selectedOnly?: boolean): Thenable { throw ni(); } } @@ -617,6 +621,9 @@ export interface MainThreadAccountManagementShape extends IDisposable { $getAccountsForProvider(providerId: string): Thenable; } +export interface MainThreadAzureBlobShape extends IDisposable { + +} export interface MainThreadAzureAccountShape extends IDisposable { } @@ -724,6 +731,7 @@ export const SqlMainContext = { MainThreadNotebookDocumentsAndEditors: createMainId('MainThreadNotebookDocumentsAndEditors'), MainThreadExtensionManagement: createMainId('MainThreadExtensionManagement'), MainThreadWorkspace: createMainId('MainThreadWorkspace'), + MainThreadAzureBlob: createMainId('MainThreadAzureBlob'), }; export const SqlExtHostContext = { @@ -747,6 +755,7 @@ export const SqlExtHostContext = { ExtHostNotebookDocumentsAndEditors: createExtId('ExtHostNotebookDocumentsAndEditors'), ExtHostExtensionManagement: createExtId('ExtHostExtensionManagement'), ExtHostWorkspace: createExtId('ExtHostWorkspace'), + ExtHostAzureBlob: createExtId('ExtHostAzureBlob') }; export interface MainThreadDashboardShape extends IDisposable { diff --git a/src/sql/workbench/services/azureBlob/browser/azureBlobService.ts b/src/sql/workbench/services/azureBlob/browser/azureBlobService.ts new file mode 100644 index 0000000000..ef730639e5 --- /dev/null +++ b/src/sql/workbench/services/azureBlob/browser/azureBlobService.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as mssql from 'mssql'; +import { IAzureBlobService } from 'sql/platform/azureBlob/common/azureBlobService'; +import { MainThreadAzureBlob } from 'sql/workbench/api/browser/mainThreadAzureBlob'; + +export class AzureBlobService implements IAzureBlobService { + + public _serviceBrand: undefined; + private _proxy: MainThreadAzureBlob; + + /** + * Internal use only, do not call! This is called once on startup by the proxy object used + * to communicate with the extension host once it's been created. + * @param proxy The proxy to use to communicate with the mssql extension + */ + public registerProxy(proxy: MainThreadAzureBlob) { + this._proxy = proxy; + } + + public createSas(connectionUri: string, blobContainerUri: string, blobContainerKey: string, storageAccountName: string, expirationDate: string): Promise { + this.checkProxy(); + return Promise.resolve(this._proxy.createSas(connectionUri, blobContainerUri, blobContainerKey, storageAccountName, expirationDate)); + } + + private checkProxy(): void { + if (!this._proxy) { + throw new Error('Azure Blob proxy not initialized'); + } + } +} diff --git a/src/typings/refs.d.ts b/src/typings/refs.d.ts index 386b867974..6bcd896038 100644 --- a/src/typings/refs.d.ts +++ b/src/typings/refs.d.ts @@ -4,3 +4,4 @@ *--------------------------------------------------------------------------------------------*/ /// +/// diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 5b8f25ee53..cb601df08d 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -180,6 +180,8 @@ import { IJobManagementService } from 'sql/workbench/services/jobManagement/comm import { JobManagementService } from 'sql/workbench/services/jobManagement/common/jobManagementService'; import { IBackupService } from 'sql/platform/backup/common/backupService'; import { BackupService } from 'sql/platform/backup/common/backupServiceImp'; +import { IAzureBlobService } from 'sql/platform/azureBlob/common/azureBlobService'; +import { AzureBlobService } from 'sql/workbench/services/azureBlob/browser/azureBlobService'; import { IBackupUiService } from 'sql/workbench/contrib/backup/common/backupUiService'; import { BackupUiService } from 'sql/workbench/contrib/backup/browser/backupUiService'; import { IRestoreDialogController, IRestoreService } from 'sql/workbench/services/restore/common/restoreService'; @@ -229,6 +231,7 @@ registerSingleton(IMetadataService, MetadataService); registerSingleton(IAdminService, AdminService); registerSingleton(IJobManagementService, JobManagementService); registerSingleton(IBackupService, BackupService); +registerSingleton(IAzureBlobService, AzureBlobService); registerSingleton(IBackupUiService, BackupUiService); registerSingleton(IScriptingService, ScriptingService); registerSingleton(IRestoreService, RestoreService); diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index cee00168a5..106648cf2b 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -84,12 +84,15 @@ import { IClipboardService as sqlIClipboardService } from 'sql/platform/clipboar import { ClipboardService as sqlClipboardService } from 'sql/platform/clipboard/electron-browser/clipboardService'; import { IQueryHistoryService } from 'sql/workbench/services/queryHistory/common/queryHistoryService'; import { QueryHistoryService } from 'sql/workbench/services/queryHistory/common/queryHistoryServiceImpl'; +import { IAzureBlobService } from 'sql/platform/azureBlob/common/azureBlobService'; +import { AzureBlobService } from 'sql/workbench/services/azureBlob/browser/azureBlobService'; import { IAzureAccountService } from 'sql/platform/azureAccount/common/azureAccountService'; import { AzureAccountService } from 'sql/workbench/services/azureAccount/browser/azureAccountService'; registerSingleton(ISqlOAuthService, SqlOAuthService); registerSingleton(sqlIClipboardService, sqlClipboardService); registerSingleton(IQueryHistoryService, QueryHistoryService); +registerSingleton(IAzureBlobService, AzureBlobService); registerSingleton(IAzureAccountService, AzureAccountService); // {{SQL CARBON EDIT}} - End