From 7f388dd42053b2f2efd4653dffde3c8e8ca0e8f2 Mon Sep 17 00:00:00 2001 From: Charles Gagnon Date: Mon, 24 Apr 2023 10:39:02 -0700 Subject: [PATCH] Add azurecore HTTP typings (#22828) * Add azurecore HTTP typings * undo + spelling fix --- .../src/account-provider/auths/httpClient.ts | 43 +++---- .../account-provider/auths/networkUtils.ts | 14 --- .../azurecore/src/azureResource/utils.ts | 77 ++++++++---- extensions/azurecore/src/azurecore.d.ts | 24 +++- extensions/azurecore/src/extension.ts | 4 +- extensions/machine-learning/src/test/stubs.ts | 2 +- extensions/sql-migration/src/api/azure.ts | 112 +++++++++--------- 7 files changed, 158 insertions(+), 118 deletions(-) diff --git a/extensions/azurecore/src/account-provider/auths/httpClient.ts b/extensions/azurecore/src/account-provider/auths/httpClient.ts index d44a340c12..78a7282547 100644 --- a/extensions/azurecore/src/account-provider/auths/httpClient.ts +++ b/extensions/azurecore/src/account-provider/auths/httpClient.ts @@ -3,10 +3,11 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { AzureNetworkResponse } from 'azurecore'; import * as http from 'http'; import * as https from 'https'; import { TextEncoder } from 'util'; -import { getNetworkResponse, NetworkRequestOptions, NetworkResponse, urlToHttpOptions } from './networkUtils'; +import { NetworkRequestOptions, urlToHttpOptions } from './networkUtils'; /** * http methods @@ -59,7 +60,7 @@ export class HttpClient { url: string, options?: NetworkRequestOptions, cancellationToken?: number | undefined - ): Promise> { + ): Promise> { if (this.proxyUrl) { return networkRequestViaProxy(url, this.proxyUrl, HttpMethod.GET, options, this.customAgentOptions as http.AgentOptions, cancellationToken); } else { @@ -76,7 +77,7 @@ export class HttpClient { url: string, options?: NetworkRequestOptions, cancellationToken?: number - ): Promise> { + ): Promise> { if (this.proxyUrl) { return networkRequestViaProxy(url, this.proxyUrl, HttpMethod.POST, options, this.customAgentOptions as http.AgentOptions, cancellationToken); } else { @@ -93,7 +94,7 @@ export class HttpClient { url: string, options?: NetworkRequestOptions, cancellationToken?: number - ): Promise> { + ): Promise> { if (this.proxyUrl) { return networkRequestViaProxy(url, this.proxyUrl, HttpMethod.PUT, options, this.customAgentOptions as http.AgentOptions, cancellationToken); } else { @@ -109,7 +110,7 @@ export class HttpClient { async sendDeleteRequestAsync( url: string, options?: NetworkRequestOptions - ): Promise> { + ): Promise> { if (this.proxyUrl) { return networkRequestViaProxy(url, this.proxyUrl, HttpMethod.DELETE, options, this.customAgentOptions as http.AgentOptions); } else { @@ -126,7 +127,7 @@ const networkRequestViaProxy = ( options?: NetworkRequestOptions, agentOptions?: http.AgentOptions, timeout?: number -): Promise> => { +): Promise> => { const destinationUrl = new URL(destinationUrlString); const proxyUrl = new URL(proxyUrlString); @@ -164,7 +165,7 @@ const networkRequestViaProxy = ( postRequestStringContent + '\r\n'; - return new Promise>(((resolve, reject) => { + return new Promise>(((resolve, reject) => { const request = http.request(tunnelRequestOptions); if (tunnelRequestOptions.timeout) { @@ -246,11 +247,11 @@ const networkRequestViaProxy = ( }); const parsedHeaders = Object.fromEntries(entries) as Record; - const networkResponse = getNetworkResponse( - parsedHeaders, - parseBody(httpStatusCode, statusMessage, parsedHeaders, body) as T, - httpStatusCode - ); + const networkResponse: AzureNetworkResponse = { + headers: parsedHeaders, + data: parseBody(httpStatusCode, statusMessage, parsedHeaders, body) as T, + status: httpStatusCode + }; if (((httpStatusCode < HttpStatus.SUCCESS_RANGE_START) || (httpStatusCode > HttpStatus.SUCCESS_RANGE_END)) && @@ -282,7 +283,7 @@ const networkRequestViaHttps = ( options?: NetworkRequestOptions, agentOptions?: https.AgentOptions, timeout?: number -): Promise> => { +): Promise> => { const isPostRequest = httpMethod === HttpMethod.POST; const isPutRequest = httpMethod === HttpMethod.PUT; // Note: Text Encoder is necessary here because otherwise it was not able to handle Chinese characters in table names. @@ -311,13 +312,13 @@ const networkRequestViaHttps = ( }; } - return new Promise>((resolve, reject) => { + return new Promise>((resolve, reject) => { const request = https.request(customOptions); if (timeout) { request.on('timeout', () => { request.destroy(); - reject(new Error('Request time out')); + reject(new Error('Request timed out')); }); } @@ -342,11 +343,11 @@ const networkRequestViaHttps = ( const dataBody = Buffer.concat([...data]).toString(); const parsedHeaders = headers as Record; - const networkResponse = getNetworkResponse( - parsedHeaders, - parseBody(statusCode, statusMessage, parsedHeaders, dataBody) as T, - statusCode - ); + const networkResponse: AzureNetworkResponse = { + headers: parsedHeaders, + data: parseBody(statusCode, statusMessage, parsedHeaders, dataBody) as T, + status: statusCode + }; if (((statusCode < HttpStatus.SUCCESS_RANGE_START) || (statusCode > HttpStatus.SUCCESS_RANGE_END)) && // do not destroy the request for the device code flow @@ -401,7 +402,7 @@ const parseBody = (statusCode: number, statusMessage: string | undefined, header parsedBody = { error: errorType, - error_description: `${errorDescriptionHelper} error occured.\nHttp status code: ${statusCode}\nHttp status message: ${statusMessage || 'Unknown'}\nHeaders: ${JSON.stringify(headers)}` + error_description: `${errorDescriptionHelper} error occurred.\nHttp status code: ${statusCode}\nHttp status message: ${statusMessage || 'Unknown'}\nHeaders: ${JSON.stringify(headers)}` }; } diff --git a/extensions/azurecore/src/account-provider/auths/networkUtils.ts b/extensions/azurecore/src/account-provider/auths/networkUtils.ts index b395a5ec1c..e2e96f15da 100644 --- a/extensions/azurecore/src/account-provider/auths/networkUtils.ts +++ b/extensions/azurecore/src/account-provider/auths/networkUtils.ts @@ -6,20 +6,6 @@ import * as azdata from 'azdata'; import * as https from 'https'; -export function getNetworkResponse(headers: Record, body: Body, statusCode: number): NetworkResponse { - return { - headers: headers, - data: body, - status: statusCode - }; -} - -export declare type NetworkResponse = { - headers: Record; - data: T; - status: number; -}; - export declare type NetworkRequestOptions = { headers?: Record; body?: string; diff --git a/extensions/azurecore/src/azureResource/utils.ts b/extensions/azurecore/src/azureResource/utils.ts index d0a89c3a2a..d4e9c277bc 100644 --- a/extensions/azurecore/src/azureResource/utils.ts +++ b/extensions/azurecore/src/azureResource/utils.ts @@ -6,7 +6,7 @@ import { ResourceGraphClient } from '@azure/arm-resourcegraph'; import { TokenCredentials } from '@azure/ms-rest-js'; import * as azdata from 'azdata'; -import { AzureRestResponse, GetResourceGroupsResult, GetSubscriptionsResult, ResourceQueryResult, GetBlobContainersResult, GetFileSharesResult, HttpRequestMethod, GetLocationsResult, GetManagedDatabasesResult, CreateResourceGroupResult, GetBlobsResult, GetStorageAccountAccessKeyResult, AzureAccount, azureResource, AzureAccountProviderMetadata } from 'azurecore'; +import { AzureRestResponse, GetResourceGroupsResult, GetSubscriptionsResult, ResourceQueryResult, GetBlobContainersResult, GetFileSharesResult, HttpRequestMethod, GetLocationsResult, GetManagedDatabasesResult, CreateResourceGroupResult, GetBlobsResult, GetStorageAccountAccessKeyResult, AzureAccount, azureResource, AzureAccountProviderMetadata, AzureNetworkResponse, HttpClientResponse } from 'azurecore'; import { EOL } from 'os'; import * as nls from 'vscode-nls'; import { AppContext } from '../appContext'; @@ -23,11 +23,31 @@ import { NetworkRequestOptions } from '@azure/msal-common'; const localize = nls.loadMessageBundle(); -export interface HttpClientResponse { - body: any; - headers: any; - status: Number; - error: any; +/** + * Shape of list operation responses + * e.g. https://learn.microsoft.com/en-us/rest/api/storagerp/srp_json_list_operations#response-body + */ +declare type AzureListOperationResponse = { + value: T; +} + +/** + * An access key for the storage account. + * https://learn.microsoft.com/en-us/rest/api/storagerp/storage-accounts/list-keys?tabs=HTTP#storageaccountkey + */ +declare type StorageAccountKey = { + creationTime: string; + keyName: string; + // permissions: KeyPermission Can add this if ever needed + value: string; +} + +/** + * The response from the ListKeys operation. + * https://learn.microsoft.com/en-us/rest/api/storagerp/storage-accounts/list-keys?tabs=HTTP#storageaccountlistkeysresult + */ +declare type StorageAccountListKeysResult = { + keys: StorageAccountKey[]; } function getErrorMessage(error: Error | string): string { @@ -170,8 +190,8 @@ export async function getLocations(appContext: AppContext, account?: AzureAccoun try { const path = `/subscriptions/${subscription.id}/locations?api-version=2020-01-01`; const host = getProviderMetadataForAccount(account).settings.armResource.endpoint; - const response = await makeHttpRequest(account, subscription, path, HttpRequestMethod.GET, undefined, ignoreErrors, host); - result.locations.push(...response.response.data.value); + const response = await makeHttpRequest>(account, subscription, path, HttpRequestMethod.GET, undefined, ignoreErrors, host); + result.locations.push(...response.response?.data.value || []); result.errors.push(...response.errors); } catch (err) { const error = new Error(localize('azure.accounts.getLocations.queryError', "Error fetching locations for account {0} ({1}) subscription {2} ({3}) tenant {4} : {5}", @@ -348,8 +368,17 @@ export async function getSelectedSubscriptions(appContext: AppContext, account?: * @param host Use this to override the host. The default host is https://management.azure.com * @param requestHeaders Provide additional request headers */ -export async function makeHttpRequest(account: AzureAccount, subscription: azureResource.AzureResourceSubscription, path: string, requestType: HttpRequestMethod, requestBody?: any, ignoreErrors: boolean = false, host: string = 'https://management.azure.com', requestHeaders: { [key: string]: string } = {}): Promise { - const result: AzureRestResponse = { response: {}, errors: [] }; +export async function makeHttpRequest( + account: AzureAccount, + subscription: azureResource.AzureResourceSubscription, + path: string, + requestType: HttpRequestMethod, + requestBody?: any, + ignoreErrors: boolean = false, + host: string = 'https://management.azure.com', + requestHeaders: Record = {} +): Promise> { + const result: AzureRestResponse = { response: undefined, errors: [] }; const httpClient: HttpClient = getProxyEnabledHttpClient(); if (!account?.properties?.tenants || !Array.isArray(account.properties.tenants)) { @@ -414,21 +443,21 @@ export async function makeHttpRequest(account: AzureAccount, subscription: azure requestUrl = `${account.properties.providerSettings.settings.armResource.endpoint}${path}`; } - let response; + let response: AzureNetworkResponse> | undefined = undefined; switch (requestType) { case HttpRequestMethod.GET: - response = await httpClient.sendGetRequestAsync(requestUrl, { + response = await httpClient.sendGetRequestAsync>(requestUrl, { headers: reqHeaders }); break; case HttpRequestMethod.POST: - response = await httpClient.sendPostRequestAsync(requestUrl, networkRequestOptions); + response = await httpClient.sendPostRequestAsync>(requestUrl, networkRequestOptions); break; case HttpRequestMethod.PUT: - response = await httpClient.sendPutRequestAsync(requestUrl, networkRequestOptions); + response = await httpClient.sendPutRequestAsync>(requestUrl, networkRequestOptions); break; case HttpRequestMethod.DELETE: - response = await httpClient.sendDeleteRequestAsync(requestUrl, { + response = await httpClient.sendDeleteRequestAsync>(requestUrl, { headers: reqHeaders }); break; @@ -459,7 +488,13 @@ export async function makeHttpRequest(account: AzureAccount, subscription: azure result.errors.push(error); } - result.response = response; + if (response) { + result.response = { + headers: response.headers, + data: response.data.body, + status: response.status + }; + } return result; } @@ -467,7 +502,7 @@ export async function makeHttpRequest(account: AzureAccount, subscription: azure export async function getManagedDatabases(account: AzureAccount, subscription: azureResource.AzureResourceSubscription, managedInstance: azureResource.AzureSqlManagedInstance, ignoreErrors: boolean): Promise { const path = `/subscriptions/${subscription.id}/resourceGroups/${managedInstance.resourceGroup}/providers/Microsoft.Sql/managedInstances/${managedInstance.name}/databases?api-version=2020-02-02-preview`; const host = getProviderMetadataForAccount(account).settings.armResource.endpoint; - const response = await makeHttpRequest(account, subscription, path, HttpRequestMethod.GET, undefined, ignoreErrors, host); + const response = await makeHttpRequest>(account, subscription, path, HttpRequestMethod.GET, undefined, ignoreErrors, host); return { databases: response?.response?.data?.value ?? [], errors: response.errors ? response.errors : [] @@ -477,7 +512,7 @@ export async function getManagedDatabases(account: AzureAccount, subscription: a export async function getBlobContainers(account: AzureAccount, subscription: azureResource.AzureResourceSubscription, storageAccount: azureResource.AzureGraphResource, ignoreErrors: boolean): Promise { const path = `/subscriptions/${subscription.id}/resourceGroups/${storageAccount.resourceGroup}/providers/Microsoft.Storage/storageAccounts/${storageAccount.name}/blobServices/default/containers?api-version=2019-06-01`; const host = getProviderMetadataForAccount(account).settings.armResource.endpoint; - const response = await makeHttpRequest(account, subscription, path, HttpRequestMethod.GET, undefined, ignoreErrors, host); + const response = await makeHttpRequest>(account, subscription, path, HttpRequestMethod.GET, undefined, ignoreErrors, host); return { blobContainers: response?.response?.data?.value ?? [], errors: response.errors ? response.errors : [] @@ -487,7 +522,7 @@ export async function getBlobContainers(account: AzureAccount, subscription: azu export async function getFileShares(account: AzureAccount, subscription: azureResource.AzureResourceSubscription, storageAccount: azureResource.AzureGraphResource, ignoreErrors: boolean): Promise { const path = `/subscriptions/${subscription.id}/resourceGroups/${storageAccount.resourceGroup}/providers/Microsoft.Storage/storageAccounts/${storageAccount.name}/fileServices/default/shares?api-version=2019-06-01`; const host = getProviderMetadataForAccount(account).settings.armResource.endpoint; - const response = await makeHttpRequest(account, subscription, path, HttpRequestMethod.GET, undefined, ignoreErrors, host); + const response = await makeHttpRequest>(account, subscription, path, HttpRequestMethod.GET, undefined, ignoreErrors, host); return { fileShares: response?.response?.data?.value ?? [], errors: response.errors ? response.errors : [] @@ -500,7 +535,7 @@ export async function createResourceGroup(account: AzureAccount, subscription: a location: location }; const host = getProviderMetadataForAccount(account).settings.armResource.endpoint; - const response = await makeHttpRequest(account, subscription, path, HttpRequestMethod.PUT, requestBody, ignoreErrors, host); + const response = await makeHttpRequest(account, subscription, path, HttpRequestMethod.PUT, requestBody, ignoreErrors, host); return { resourceGroup: response?.response?.data, errors: response.errors ? response.errors : [] @@ -510,7 +545,7 @@ export async function createResourceGroup(account: AzureAccount, subscription: a export async function getStorageAccountAccessKey(account: AzureAccount, subscription: azureResource.AzureResourceSubscription, storageAccount: azureResource.AzureGraphResource, ignoreErrors: boolean): Promise { const path = `/subscriptions/${subscription.id}/resourceGroups/${storageAccount.resourceGroup}/providers/Microsoft.Storage/storageAccounts/${storageAccount.name}/listKeys?api-version=2019-06-01`; const host = getProviderMetadataForAccount(account).settings.armResource.endpoint; - const response = await makeHttpRequest(account, subscription, path, HttpRequestMethod.POST, undefined, ignoreErrors, host); + const response = await makeHttpRequest(account, subscription, path, HttpRequestMethod.POST, undefined, ignoreErrors, host); return { keyName1: response?.response?.data?.keys[0].value ?? '', keyName2: response?.response?.data?.keys[0].value ?? '', diff --git a/extensions/azurecore/src/azurecore.d.ts b/extensions/azurecore/src/azurecore.d.ts index 14bd2c4f16..3cd729daca 100644 --- a/extensions/azurecore/src/azurecore.d.ts +++ b/extensions/azurecore/src/azurecore.d.ts @@ -276,6 +276,24 @@ declare module 'azurecore' { DELETE } + /** + * Custom version of NetworkResponse from @azure\msal-common\dist\network\NetworkManager.d.ts + * with body renamed to data to avoid breaking changes with extensions. See + * https://github.com/microsoft/azuredatastudio/pull/22761 for details. + */ + export type AzureNetworkResponse = { + headers: Record; + data: T; + status: number; + }; + + export interface HttpClientResponse { + body: B; + headers: any; + status: Number; + error: any; + } + export interface IExtension { /** * Gets the list of subscriptions for the specified AzureAccount @@ -308,7 +326,7 @@ declare module 'azurecore' { * @param host Use this to override the host. The default host is https://management.azure.com * @param requestHeaders Provide additional request headers */ - makeAzureRestRequest(account: AzureAccount, subscription: azureResource.AzureResourceSubscription, path: string, requestType: HttpRequestMethod, requestBody?: any, ignoreErrors?: boolean, host?: string, requestHeaders?: { [key: string]: string }): Promise; + makeAzureRestRequest(account: AzureAccount, subscription: azureResource.AzureResourceSubscription, path: string, requestType: HttpRequestMethod, requestBody?: any, ignoreErrors?: boolean, host?: string, requestHeaders?: Record): Promise>; /** * Converts a region value (@see AzureRegion) into the localized Display Name * @param region The region value @@ -339,9 +357,9 @@ declare module 'azurecore' { export type GetStorageAccountResult = { resources: azureResource.AzureGraphResource[], errors: Error[] }; export type GetBlobContainersResult = { blobContainers: azureResource.BlobContainer[], errors: Error[] }; export type GetFileSharesResult = { fileShares: azureResource.FileShare[], errors: Error[] }; - export type CreateResourceGroupResult = { resourceGroup: azureResource.AzureResourceResourceGroup, errors: Error[] }; + export type CreateResourceGroupResult = { resourceGroup: azureResource.AzureResourceResourceGroup | undefined, errors: Error[] }; export type ResourceQueryResult = { resources: T[], errors: Error[] }; - export type AzureRestResponse = { response: any, errors: Error[] }; + export type AzureRestResponse = { response: AzureNetworkResponse | undefined, errors: Error[] }; export type GetBlobsResult = { blobs: azureResource.Blob[], errors: Error[] }; export type GetStorageAccountAccessKeyResult = { keyName1: string, keyName2: string, errors: Error[] }; export type CacheEncryptionKeys = { key: string; iv: string; } diff --git a/extensions/azurecore/src/extension.ts b/extensions/azurecore/src/extension.ts index 72b8baba42..a235c218a7 100644 --- a/extensions/azurecore/src/extension.ts +++ b/extensions/azurecore/src/extension.ts @@ -230,14 +230,14 @@ export async function activate(context: vscode.ExtensionContext): Promise { return azureResourceUtils.createResourceGroup(account, subscription, resourceGroupName, location, ignoreErrors); }, - makeAzureRestRequest(account: azurecore.AzureAccount, + makeAzureRestRequest(account: azurecore.AzureAccount, subscription: azurecore.azureResource.AzureResourceSubscription, path: string, requestType: azurecore.HttpRequestMethod, requestBody: any, ignoreErrors: boolean, host: string = 'https://management.azure.com', - requestHeaders: { [key: string]: string } = {}): Promise { + requestHeaders: Record = {}): Promise> { return azureResourceUtils.makeHttpRequest(account, subscription, path, requestType, requestBody, ignoreErrors, host, requestHeaders); }, getRegionDisplayName: utils.getRegionDisplayName, diff --git a/extensions/machine-learning/src/test/stubs.ts b/extensions/machine-learning/src/test/stubs.ts index 483bae7352..aaafa8ed58 100644 --- a/extensions/machine-learning/src/test/stubs.ts +++ b/extensions/machine-learning/src/test/stubs.ts @@ -22,7 +22,7 @@ export class AzurecoreApiStub implements azurecore.IExtension { getLocations(_account?: azdata.Account, _subscription?: azurecore.azureResource.AzureResourceSubscription, _ignoreErrors?: boolean): Promise { throw new Error('Method not implemented.'); } - makeAzureRestRequest(_account: azdata.Account, _subscription: azurecore.azureResource.AzureResourceSubscription, _serviceUrl: string, _requestType: azurecore.HttpRequestMethod, _requestBody?: any, _ignoreErrors?: boolean): Promise { + makeAzureRestRequest(_account: azdata.Account, _subscription: azurecore.azureResource.AzureResourceSubscription, _serviceUrl: string, _requestType: azurecore.HttpRequestMethod, _requestBody?: any, _ignoreErrors?: boolean): Promise> { throw new Error('Method not implemented.'); } getFileShares(_account: azdata.Account, _subscription: azurecore.azureResource.AzureResourceSubscription, _storageAccount: azurecore.azureResource.AzureGraphResource, _ignoreErrors?: boolean): Promise { diff --git a/extensions/sql-migration/src/api/azure.ts b/extensions/sql-migration/src/api/azure.ts index a1482f0a74..ae6327a78c 100644 --- a/extensions/sql-migration/src/api/azure.ts +++ b/extensions/sql-migration/src/api/azure.ts @@ -43,7 +43,7 @@ export async function getLocations(account: azdata.Account, subscription: Subscr const path = `/subscriptions/${subscription.id}/providers/Microsoft.DataMigration?api-version=${ARM_MGMT_API_VERSION}`; const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const dataMigrationResourceProvider = (await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host))?.response?.data; + const dataMigrationResourceProvider = (await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host))?.response?.data; const sqlMigratonResource = dataMigrationResourceProvider?.resourceTypes?.find((r: any) => r.resourceType === 'SqlMigrationServices'); const sqlMigrationResourceLocations = sqlMigratonResource?.locations ?? []; if (response.errors?.length > 0) { @@ -70,7 +70,7 @@ export async function getResourceGroups(account: azdata.Account, subscription: S return result.resourceGroups; } -export async function createResourceGroup(account: azdata.Account, subscription: Subscription, resourceGroupName: string, location: string): Promise { +export async function createResourceGroup(account: azdata.Account, subscription: Subscription, resourceGroupName: string, location: string): Promise { const api = await getAzureCoreAPI(); const result = await api.createResourceGroup(account, subscription, resourceGroupName, location, false); return result.resourceGroup; @@ -238,37 +238,37 @@ export async function getAvailableSqlDatabaseServers(account: azdata.Account, su const api = await getAzureCoreAPI(); const path = encodeURI(`/subscriptions/${subscription.id}/providers/Microsoft.Sql/servers?api-version=${SQL_SQLDB_API_VERSION}`); const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); if (response.errors.length > 0) { const message = response.errors .map(err => err.message) .join(', '); throw new Error(message); } - sortResourceArrayByName(response.response.data.value); - return response.response.data.value; + sortResourceArrayByName(response.response!.data.value); + return response.response!.data.value; } export async function getAvailableSqlDatabases(account: azdata.Account, subscription: Subscription, resourceGroupName: string, serverName: string): Promise { const api = await getAzureCoreAPI(); const path = encodeURI(`/subscriptions/${subscription.id}/resourceGroups/${resourceGroupName}/providers/Microsoft.Sql/servers/${serverName}/databases?api-version=${SQL_SQLDB_API_VERSION}`); const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); if (response.errors.length > 0) { const message = response.errors .map(err => err.message) .join(', '); throw new Error(message); } - sortResourceArrayByName(response.response.data.value); - return response.response.data.value; + sortResourceArrayByName(response.response!.data.value); + return response.response!.data.value; } export async function getAvailableSqlVMs(account: azdata.Account, subscription: Subscription): Promise { const api = await getAzureCoreAPI(); const path = encodeURI(`/subscriptions/${subscription.id}/providers/Microsoft.SqlVirtualMachine/sqlVirtualMachines?api-version=${SQL_VM_API_VERSION}`); const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); if (response.errors.length > 0) { const message = response.errors @@ -276,15 +276,15 @@ export async function getAvailableSqlVMs(account: azdata.Account, subscription: .join(', '); throw new Error(message); } - sortResourceArrayByName(response.response.data.value); - return response.response.data.value; + sortResourceArrayByName(response.response!.data.value); + return response.response!.data.value; } export async function getVMInstanceView(sqlVm: SqlVMServer, account: azdata.Account, subscription: Subscription): Promise { const api = await getAzureCoreAPI(); const path = encodeURI(`/subscriptions/${subscription.id}/resourceGroups/${getResourceGroupFromId(sqlVm.id)}/providers/Microsoft.Compute/virtualMachines/${sqlVm.name}/instanceView?api-version=${COMPUTE_VM_API_VERSION}`); const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); if (response.errors.length > 0) { const message = response.errors @@ -294,14 +294,14 @@ export async function getVMInstanceView(sqlVm: SqlVMServer, account: azdata.Acco } - return response.response.data; + return response.response!.data; } export async function getAzureResourceGivenId(account: azdata.Account, subscription: Subscription, id: string, apiVersion: string): Promise { const api = await getAzureCoreAPI(); const path = encodeURI(`${id}?api-version=${apiVersion}`); const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); if (response.errors.length > 0) { const message = response.errors @@ -311,7 +311,7 @@ export async function getAzureResourceGivenId(account: azdata.Account, subscript } - return response.response.data; + return response.response!.data; } export async function getComputeVM(sqlVm: SqlVMServer, account: azdata.Account, subscription: Subscription): Promise { @@ -360,51 +360,51 @@ export async function getSqlMigrationServiceById(account: azdata.Account, subscr const api = await getAzureCoreAPI(); const path = encodeURI(`${sqlMigrationServiceId}?api-version=${DMSV2_API_VERSION}`); const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); if (response.errors.length > 0) { const message = response.errors .map(err => err.message) .join(', '); throw new Error(message); } - response.response.data.properties.resourceGroup = getResourceGroupFromId(response.response.data.id); - return response.response.data; + response.response!.data.properties.resourceGroup = getResourceGroupFromId(response.response!.data.id); + return response.response!.data; } export async function getSqlMigrationServicesByResourceGroup(account: azdata.Account, subscription: Subscription, resouceGroupName: string): Promise { const api = await getAzureCoreAPI(); const path = encodeURI(`/subscriptions/${subscription.id}/resourceGroups/${resouceGroupName}/providers/Microsoft.DataMigration/sqlMigrationServices?api-version=${DMSV2_API_VERSION}`); const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); if (response.errors.length > 0) { const message = response.errors .map(err => err.message) .join(', '); throw new Error(message); } - sortResourceArrayByName(response.response.data.value); - response.response.data.value.forEach((sms: SqlMigrationService) => { + sortResourceArrayByName(response.response!.data.value); + response.response!.data.value.forEach((sms: SqlMigrationService) => { sms.properties.resourceGroup = getResourceGroupFromId(sms.id); }); - return response.response.data.value; + return response.response!.data.value; } export async function getSqlMigrationServices(account: azdata.Account, subscription: Subscription): Promise { const api = await getAzureCoreAPI(); const path = encodeURI(`/subscriptions/${subscription.id}/providers/Microsoft.DataMigration/sqlMigrationServices?api-version=${DMSV2_API_VERSION}`); const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); if (response.errors.length > 0) { const message = response.errors .map(err => err.message) .join(', '); throw new Error(message); } - sortResourceArrayByName(response.response.data.value); - response.response.data.value.forEach((sms: SqlMigrationService) => { + sortResourceArrayByName(response.response!.data.value); + response.response!.data.value.forEach((sms: SqlMigrationService) => { sms.properties.resourceGroup = getResourceGroupFromId(sms.id); }); - return response.response.data.value; + return response.response!.data.value; } export async function createSqlMigrationService(account: azdata.Account, subscription: Subscription, resourceGroupName: string, regionName: string, sqlMigrationServiceName: string, sessionId: string): Promise { @@ -414,21 +414,21 @@ export async function createSqlMigrationService(account: azdata.Account, subscri const requestBody = { 'location': regionName }; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.PUT, requestBody, true, host, getSessionIdHeader(sessionId)); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.PUT, requestBody, true, host, getSessionIdHeader(sessionId)); if (response.errors.length > 0) { const message = response.errors .map(err => err.message) .join(', '); throw new Error(message); } - const asyncUrl = response.response.headers['azure-asyncoperation']; + const asyncUrl = response.response!.headers['azure-asyncoperation']; const asyncPath = asyncUrl.replace((new URL(asyncUrl)).origin + '/', ''); // path is everything after the hostname, e.g. the 'test' part of 'https://management.azure.com/test' const maxRetry = 24; let i = 0; for (i = 0; i < maxRetry; i++) { - const asyncResponse = await api.makeAzureRestRequest(account, subscription, asyncPath, azurecore.HttpRequestMethod.GET, undefined, true, host); - const creationStatus = asyncResponse.response.data.status; + const asyncResponse = await api.makeAzureRestRequest(account, subscription, asyncPath, azurecore.HttpRequestMethod.GET, undefined, true, host); + const creationStatus = asyncResponse.response!.data.status; if (creationStatus === constants.ProvisioningState.Succeeded) { break; } else if (creationStatus === constants.ProvisioningState.Failed) { @@ -439,14 +439,14 @@ export async function createSqlMigrationService(account: azdata.Account, subscri if (i === maxRetry) { throw new Error(constants.DMS_PROVISIONING_FAILED); } - return response.response.data; + return response.response!.data; } export async function getSqlMigrationServiceAuthKeys(account: azdata.Account, subscription: Subscription, resourceGroupName: string, regionName: string, sqlMigrationServiceName: string): Promise { const api = await getAzureCoreAPI(); const path = encodeURI(`/subscriptions/${subscription.id}/resourceGroups/${resourceGroupName}/providers/Microsoft.DataMigration/sqlMigrationServices/${sqlMigrationServiceName}/ListAuthKeys?api-version=${DMSV2_API_VERSION}`); const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.POST, undefined, true, host); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.POST, undefined, true, host); if (response.errors.length > 0) { const message = response.errors .map(err => err.message) @@ -467,7 +467,7 @@ export async function regenerateSqlMigrationServiceAuthKey(account: azdata.Accou 'keyName': keyName, }; const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.POST, requestBody, true, host); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.POST, requestBody, true, host); if (response.errors.length > 0) { const message = response.errors .map(err => err.message) @@ -499,14 +499,14 @@ export async function getSqlMigrationServiceMonitoringData(account: azdata.Accou const api = await getAzureCoreAPI(); const path = encodeURI(`/subscriptions/${subscription.id}/resourceGroups/${resourceGroupName}/providers/Microsoft.DataMigration/sqlMigrationServices/${sqlMigrationService}/listMonitoringData?api-version=${DMSV2_API_VERSION}`); const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.POST, undefined, true, host); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.POST, undefined, true, host); if (response.errors.length > 0) { const message = response.errors .map(err => err.message) .join(', '); throw new Error(message); } - return response.response.data; + return response.response!.data; } export async function startDatabaseMigration( @@ -521,18 +521,18 @@ export async function startDatabaseMigration( const api = await getAzureCoreAPI(); const path = encodeURI(`${targetServer.id}/providers/Microsoft.DataMigration/databaseMigrations/${targetDatabaseName}?api-version=${DMSV2_API_VERSION}`); const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.PUT, requestBody, true, host, getSessionIdHeader(sessionId)); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.PUT, requestBody, true, host, getSessionIdHeader(sessionId)); if (response.errors.length > 0) { const message = response.errors .map(err => err.message) .join(', '); throw new Error(message); } - const asyncUrl = response.response.headers['azure-asyncoperation']; + const asyncUrl = response.response!.headers['azure-asyncoperation']; return { asyncUrl: asyncUrl, - status: response.response.status, - databaseMigration: response.response.data + status: response.response!.status, + databaseMigration: response.response!.data }; } @@ -544,7 +544,7 @@ export async function getMigrationDetails(account: azdata.Account, subscription: const api = await getAzureCoreAPI(); const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); if (response.errors.length > 0) { const message = response.errors .map(err => err.message) @@ -552,14 +552,14 @@ export async function getMigrationDetails(account: azdata.Account, subscription: throw new Error(message); } - return response.response.data; + return response.response!.data; } export async function getServiceMigrations(account: azdata.Account, subscription: Subscription, resourceId: string): Promise { const path = encodeURI(`${resourceId}/listMigrations?&api-version=${DMSV2_API_VERSION}`); const api = await getAzureCoreAPI(); const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); if (response.errors.length > 0) { const message = response.errors .map(err => err.message) @@ -567,7 +567,7 @@ export async function getServiceMigrations(account: azdata.Account, subscription throw new Error(message); } - return response.response.data.value; + return response.response!.data.value; } export async function getMigrationTargetInstance(account: azdata.Account, subscription: Subscription, migration: DatabaseMigration): Promise { @@ -575,7 +575,7 @@ export async function getMigrationTargetInstance(account: azdata.Account, subscr const path = encodeURI(`${targetServerId}?api-version=${SQL_MI_API_VERSION}`); const api = await getAzureCoreAPI(); const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); if (response.errors.length > 0) { const message = response.errors .map(err => err.message) @@ -583,21 +583,21 @@ export async function getMigrationTargetInstance(account: azdata.Account, subscr throw new Error(message); } - return response.response.data; + return response.response!.data; } export async function getMigrationAsyncOperationDetails(account: azdata.Account, subscription: Subscription, url: string): Promise { const api = await getAzureCoreAPI(); const path = url.replace((new URL(url)).origin + '/', ''); // path is everything after the hostname, e.g. the 'test' part of 'https://management.azure.com/test' const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host); if (response.errors.length > 0) { const message = response.errors .map(err => err.message) .join(', '); throw new Error(message); } - return response.response.data; + return response.response!.data; } export async function startMigrationCutover(account: azdata.Account, subscription: Subscription, migration: DatabaseMigration): Promise { @@ -605,14 +605,14 @@ export async function startMigrationCutover(account: azdata.Account, subscriptio const path = encodeURI(`${migration.id}/cutover?api-version=${DMSV2_API_VERSION}`); const requestBody = { migrationOperationId: migration.properties.migrationOperationId }; const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.POST, requestBody, true, host); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.POST, requestBody, true, host); if (response.errors.length > 0) { const message = response.errors .map(err => err.message) .join(', '); throw new Error(message); } - return response.response.data.value; + return response.response!.data.value; } export async function stopMigration(account: azdata.Account, subscription: Subscription, migration: DatabaseMigration): Promise { @@ -620,7 +620,7 @@ export async function stopMigration(account: azdata.Account, subscription: Subsc const path = encodeURI(`${migration.id}/cancel?api-version=${DMSV2_API_VERSION}`); const requestBody = { migrationOperationId: migration.properties.migrationOperationId }; const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.POST, requestBody, true, host); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.POST, requestBody, true, host); if (response.errors.length > 0) { const message = response.errors .map(err => err.message) @@ -634,7 +634,7 @@ export async function retryMigration(account: azdata.Account, subscription: Subs const path = encodeURI(`${migration.id}/retry?api-version=${DMSV2_API_VERSION}`); const requestBody = { migrationOperationId: migration.properties.migrationOperationId }; const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.POST, requestBody, true, host); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.POST, requestBody, true, host); if (response.errors.length > 0) { const message = response.errors .map(err => err.message) @@ -647,7 +647,7 @@ export async function deleteMigration(account: azdata.Account, subscription: Sub const api = await getAzureCoreAPI(); const path = encodeURI(`${migrationId}?api-version=${DMSV2_API_VERSION}`); const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint; - const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.DELETE, undefined, true, host); + const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.DELETE, undefined, true, host); if (response.errors.length > 0) { const message = response.errors .map(err => err.message) @@ -706,7 +706,7 @@ export async function validateIrSqlDatabaseMigrationSettings( } }; - const response = await api.makeAzureRestRequest( + const response = await api.makeAzureRestRequest( account, subscription, path, @@ -718,7 +718,7 @@ export async function validateIrSqlDatabaseMigrationSettings( if (response.errors.length > 0) { throw new Error(response.errors.map(e => e.message).join(',')); } - return response.response.data; + return response.response!.data; } export async function validateIrDatabaseMigrationSettings( @@ -773,7 +773,7 @@ export async function validateIrDatabaseMigrationSettings( } }; - const response = await api.makeAzureRestRequest( + const response = await api.makeAzureRestRequest( account, subscription, path, @@ -785,7 +785,7 @@ export async function validateIrDatabaseMigrationSettings( if (response.errors.length > 0) { throw new Error(response.errors.map(e => e.message).join(',')); } - return response.response.data; + return response.response!.data; } type SortableAzureResources = AzureProduct | azurecore.azureResource.FileShare | azurecore.azureResource.BlobContainer | azurecore.azureResource.Blob | azurecore.azureResource.AzureResourceSubscription | SqlMigrationService;