diff --git a/src/postgres/postgresConstants.ts b/src/postgres/postgresConstants.ts index ca8dab8f..9bcf1514 100644 --- a/src/postgres/postgresConstants.ts +++ b/src/postgres/postgresConstants.ts @@ -5,3 +5,4 @@ export const invalidCredentialsErrorType: string = '28P01'; export const firewallNotConfiguredErrorType: string = '28000'; +export const timeoutErrorType = 'ETIMEDOUT'; diff --git a/src/postgres/tree/ClientConfigFactory.ts b/src/postgres/tree/ClientConfigFactory.ts index f52b5971..271f5b92 100644 --- a/src/postgres/tree/ClientConfigFactory.ts +++ b/src/postgres/tree/ClientConfigFactory.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { callWithTelemetryAndErrorHandling } from "@microsoft/vscode-azext-utils"; +import { callWithTelemetryAndErrorHandling, parseError } from "@microsoft/vscode-azext-utils"; import { ClientConfig } from "pg"; import { getAzureAdUserSession, getTokenFunction } from "../../azureAccountUtils"; import { localize } from "../../utils/localize"; import { PostgresClientConfigType, getClientConfigs, testClientConfig } from "../getClientConfig"; -import { invalidCredentialsErrorType } from "../postgresConstants"; +import { firewallNotConfiguredErrorType, invalidCredentialsErrorType, timeoutErrorType } from "../postgresConstants"; import { PostgresServerTreeItem } from "./PostgresServerTreeItem"; export const postgresResourceType = "https://ossrdbms-aad.database.windows.net/"; @@ -62,7 +62,19 @@ export class PostgresClientConfigFactory { clientConfig }; } catch (error) { - // If the client config failed during test, skip and try the next available one. + const parsedError = parseError(error); + if (parsedError.errorType === invalidCredentialsErrorType) { + // If the client config failed with invalid credential error, skip and try the next available one. + } else if (parsedError.errorType === firewallNotConfiguredErrorType || parsedError.errorType === timeoutErrorType) { + // The time out error are common when the firewall rules doesn't grant access from the current IP address. + // If the client is blocked by the firewall, let the user go to Azure Portal to grant access. + throw { + message: localize("mustConfigureFirewall", 'Must configure firewall from Azure Portal to grant access.'), + code: firewallNotConfiguredErrorType + }; + } else { + throw error; + } } } diff --git a/src/postgres/tree/PostgresDatabaseTreeItem.ts b/src/postgres/tree/PostgresDatabaseTreeItem.ts index cf6a51cc..05f7a441 100644 --- a/src/postgres/tree/PostgresDatabaseTreeItem.ts +++ b/src/postgres/tree/PostgresDatabaseTreeItem.ts @@ -8,7 +8,7 @@ import { AzExtParentTreeItem, AzExtTreeItem, createContextValue, GenericTreeItem import { ThemeIcon } from 'vscode'; import { ext } from '../../extensionVariables'; import { localize } from '../../utils/localize'; -import { invalidCredentialsErrorType } from '../postgresConstants'; +import { firewallNotConfiguredErrorType, invalidCredentialsErrorType } from '../postgresConstants'; import { runPostgresQuery, wrapArgInQuotes } from '../runPostgresQuery'; import { PostgresClientConfigFactory } from './ClientConfigFactory'; import { PostgresFunctionsTreeItem } from './PostgresFunctionsTreeItem'; @@ -90,6 +90,9 @@ export class PostgresDatabaseTreeItem extends AzExtParentTreeItem { }); credentialsTreeItem.commandArgs = [this.parent]; return [credentialsTreeItem]; + } else if (this.parent.azureName && parsedError.errorType === firewallNotConfiguredErrorType) { + void context.ui.showWarningMessage(localize('couldNotConnect', 'Could not connect to "{0}": {1}', this.parent.label, parsedError.message), { stepName: 'loadPostgresDatabases' }); + return []; } else { throw error; }