mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Connect to SQL from Postgres Parameters Page (#13744)
* Addition: properties page with link to dashboard * Include new page * Initial Parameter page start * Include new changes from merged PRs * Including new constants * Git errors * Add parameter commands and help * Reset command * Added chart * git fix * Fixed string issues * connectSqlDialog is an abstract class. Separated out Miaa and Postgress connection * Initial start to adding connect to sql for postgres instance * Simplified classes extending ConnectToSqlDialog, added get providerName, and function to create error message * Miaa models provides dialog title * Updated failed message parameters * completionPromise.reject * Fixed connect to MSSql * Messy dialog showing from button * removed this._completionPromise.reject * Cleaning up code * Set connectSqlDialog to be an abstract class. Separated out Miaa and Postgres. (#13532) * connectSqlDialog is an abstract class. Separated out Miaa and Postgress connection * Simplified classes extending ConnectToSqlDialog, added get providerName, and function to create error message * Miaa models provides dialog title * Updated failed message parameters * completionPromise.reject * Fixed connect to MSSql * removed this._completionPromise.reject * Connect button clean up * Format * Format doc * Fixed compile errors * Cleaning up page * Clean up * clean up refresh * Format doc * Removed ellipse * Cleaning up problems * Updating localized constants * Missing username update * Get connection profile added to Resource model, abstract method created for calling connection dialog * Added createConnectionProfile * took out import * Pulled in new changes, fixed usercancellederror * Getting engine settings * Git errors * Corrected names of icons and constants, Fixed Miaa dialog title * Changed gear svg, made postgres extension a loc constant, fixed formatting * Fixed controller model name * Put connection profile and id in resource model, changed back controller model in base class
This commit is contained in:
@@ -9,10 +9,9 @@ import * as azdataExt from 'azdata-ext';
|
||||
import * as vscode from 'vscode';
|
||||
import { UserCancelledError } from '../common/api';
|
||||
import { Deferred } from '../common/promise';
|
||||
import { createCredentialId, parseIpAndPort } from '../common/utils';
|
||||
import { credentialNamespace } from '../constants';
|
||||
import { parseIpAndPort } from '../common/utils';
|
||||
import * as loc from '../localizedConstants';
|
||||
import { ConnectToSqlDialog } from '../ui/dialogs/connectSqlDialog';
|
||||
import { ConnectToMiaaSqlDialog } from '../ui/dialogs/connectMiaaDialog';
|
||||
import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
||||
import { ControllerModel, Registration } from './controllerModel';
|
||||
import { ResourceModel } from './resourceModel';
|
||||
@@ -23,10 +22,6 @@ export class MiaaModel extends ResourceModel {
|
||||
|
||||
private _config: azdataExt.SqlMiShowResult | undefined;
|
||||
private _databases: DatabaseModel[] = [];
|
||||
// The saved connection information
|
||||
private _connectionProfile: azdata.IConnectionProfile | undefined = undefined;
|
||||
// The ID of the active connection used to query the server
|
||||
private _activeConnectionId: string | undefined = undefined;
|
||||
|
||||
private readonly _onConfigUpdated = new vscode.EventEmitter<azdataExt.SqlMiShowResult | undefined>();
|
||||
private readonly _onDatabasesUpdated = new vscode.EventEmitter<DatabaseModel[]>();
|
||||
@@ -38,8 +33,8 @@ export class MiaaModel extends ResourceModel {
|
||||
|
||||
private _refreshPromise: Deferred<void> | undefined = undefined;
|
||||
|
||||
constructor(controllerModel: ControllerModel, private _miaaInfo: MiaaResourceInfo, registration: Registration, private _treeDataProvider: AzureArcTreeDataProvider) {
|
||||
super(controllerModel, _miaaInfo, registration);
|
||||
constructor(_controllerModel: ControllerModel, private _miaaInfo: MiaaResourceInfo, registration: Registration, private _treeDataProvider: AzureArcTreeDataProvider) {
|
||||
super(_controllerModel, _miaaInfo, registration);
|
||||
this._azdataApi = <azdataExt.IExtension>vscode.extensions.getExtension(azdataExt.extension.name)?.exports;
|
||||
}
|
||||
|
||||
@@ -124,47 +119,41 @@ export class MiaaModel extends ResourceModel {
|
||||
}
|
||||
|
||||
private async getDatabases(): Promise<void> {
|
||||
await this.getConnectionProfile();
|
||||
if (this._connectionProfile) {
|
||||
// We haven't connected yet so do so now and then store the ID for the active connection
|
||||
if (!this._activeConnectionId) {
|
||||
const result = await azdata.connection.connect(this._connectionProfile, false, false);
|
||||
if (!result.connected) {
|
||||
throw new Error(result.errorMessage);
|
||||
}
|
||||
this._activeConnectionId = result.connectionId;
|
||||
}
|
||||
|
||||
const provider = azdata.dataprotocol.getProvider<azdata.MetadataProvider>(this._connectionProfile.providerName, azdata.DataProviderType.MetadataProvider);
|
||||
const ownerUri = await azdata.connection.getUriForConnection(this._activeConnectionId);
|
||||
const databases = await provider.getDatabases(ownerUri);
|
||||
if (!databases) {
|
||||
throw new Error('Could not fetch databases');
|
||||
}
|
||||
if (databases.length > 0 && typeof (databases[0]) === 'object') {
|
||||
this._databases = (<azdata.DatabaseInfo[]>databases).map(db => { return { name: db.options['name'], status: db.options['state'] }; });
|
||||
} else {
|
||||
this._databases = (<string[]>databases).map(db => { return { name: db, status: '-' }; });
|
||||
}
|
||||
this.databasesLastUpdated = new Date();
|
||||
this._onDatabasesUpdated.fire(this._databases);
|
||||
if (!this._connectionProfile) {
|
||||
await this.getConnectionProfile();
|
||||
}
|
||||
|
||||
// We haven't connected yet so do so now and then store the ID for the active connection
|
||||
if (!this._activeConnectionId) {
|
||||
const result = await azdata.connection.connect(this._connectionProfile!, false, false);
|
||||
if (!result.connected) {
|
||||
throw new Error(result.errorMessage);
|
||||
}
|
||||
this._activeConnectionId = result.connectionId;
|
||||
}
|
||||
|
||||
const provider = azdata.dataprotocol.getProvider<azdata.MetadataProvider>(this._connectionProfile!.providerName, azdata.DataProviderType.MetadataProvider);
|
||||
const ownerUri = await azdata.connection.getUriForConnection(this._activeConnectionId);
|
||||
const databases = await provider.getDatabases(ownerUri);
|
||||
if (!databases) {
|
||||
throw new Error('Could not fetch databases');
|
||||
}
|
||||
if (databases.length > 0 && typeof (databases[0]) === 'object') {
|
||||
this._databases = (<azdata.DatabaseInfo[]>databases).map(db => { return { name: db.options['name'], status: db.options['state'] }; });
|
||||
} else {
|
||||
this._databases = (<string[]>databases).map(db => { return { name: db, status: '-' }; });
|
||||
}
|
||||
this.databasesLastUpdated = new Date();
|
||||
this._onDatabasesUpdated.fire(this._databases);
|
||||
}
|
||||
/**
|
||||
* Loads the saved connection profile associated with this model. Will prompt for one if
|
||||
* we don't have one or can't find it (it was deleted)
|
||||
*/
|
||||
private async getConnectionProfile(): Promise<void> {
|
||||
if (this._connectionProfile) {
|
||||
return;
|
||||
}
|
||||
|
||||
protected createConnectionProfile(): azdata.IConnectionProfile {
|
||||
const ipAndPort = parseIpAndPort(this.config?.status.externalEndpoint || '');
|
||||
let connectionProfile: azdata.IConnectionProfile | undefined = {
|
||||
return {
|
||||
serverName: `${ipAndPort.ip},${ipAndPort.port}`,
|
||||
databaseName: '',
|
||||
authenticationType: 'SqlLogin',
|
||||
providerName: 'MSSQL',
|
||||
providerName: loc.miaaProviderName,
|
||||
connectionName: '',
|
||||
userName: this._miaaInfo.userName || '',
|
||||
password: '',
|
||||
@@ -175,47 +164,21 @@ export class MiaaModel extends ResourceModel {
|
||||
groupId: undefined,
|
||||
options: {}
|
||||
};
|
||||
}
|
||||
|
||||
// If we have the ID stored then try to retrieve the password from previous connections
|
||||
if (this.info.connectionId) {
|
||||
try {
|
||||
const credentialProvider = await azdata.credentials.getProvider(credentialNamespace);
|
||||
const credentials = await credentialProvider.readCredential(createCredentialId(this.controllerModel.info.id, this.info.resourceType, this.info.name));
|
||||
if (credentials.password) {
|
||||
// Try to connect to verify credentials are still valid
|
||||
connectionProfile.password = credentials.password;
|
||||
// If we don't have a username for some reason then just continue on and we'll prompt for the username below
|
||||
if (connectionProfile.userName) {
|
||||
const result = await azdata.connection.connect(connectionProfile, false, false);
|
||||
if (!result.connected) {
|
||||
vscode.window.showErrorMessage(loc.connectToSqlFailed(connectionProfile.serverName, result.errorMessage));
|
||||
const connectToSqlDialog = new ConnectToSqlDialog(this.controllerModel, this);
|
||||
connectToSqlDialog.showDialog(connectionProfile);
|
||||
connectionProfile = await connectToSqlDialog.waitForClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`Unexpected error fetching password for MIAA instance ${err}`);
|
||||
// ignore - something happened fetching the password so just reprompt
|
||||
}
|
||||
}
|
||||
protected async promptForConnection(connectionProfile: azdata.IConnectionProfile): Promise<void> {
|
||||
const connectToSqlDialog = new ConnectToMiaaSqlDialog(this.controllerModel, this);
|
||||
connectToSqlDialog.showDialog(loc.connectToMSSql(this.info.name), connectionProfile);
|
||||
let profileFromDialog = await connectToSqlDialog.waitForClose();
|
||||
|
||||
if (!connectionProfile?.userName || !connectionProfile?.password) {
|
||||
// Need to prompt user for password since we don't have one stored
|
||||
const connectToSqlDialog = new ConnectToSqlDialog(this.controllerModel, this);
|
||||
connectToSqlDialog.showDialog(connectionProfile);
|
||||
connectionProfile = await connectToSqlDialog.waitForClose();
|
||||
}
|
||||
|
||||
if (connectionProfile) {
|
||||
this.updateConnectionProfile(connectionProfile);
|
||||
if (profileFromDialog) {
|
||||
this.updateConnectionProfile(profileFromDialog);
|
||||
} else {
|
||||
throw new UserCancelledError();
|
||||
}
|
||||
}
|
||||
|
||||
private async updateConnectionProfile(connectionProfile: azdata.IConnectionProfile): Promise<void> {
|
||||
protected async updateConnectionProfile(connectionProfile: azdata.IConnectionProfile): Promise<void> {
|
||||
this._connectionProfile = connectionProfile;
|
||||
this.info.connectionId = connectionProfile.id;
|
||||
this._miaaInfo.userName = connectionProfile.userName;
|
||||
|
||||
@@ -3,27 +3,45 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ResourceInfo } from 'arc';
|
||||
import { PGResourceInfo } from 'arc';
|
||||
import * as azdata from 'azdata';
|
||||
import * as azdataExt from 'azdata-ext';
|
||||
import * as vscode from 'vscode';
|
||||
import * as loc from '../localizedConstants';
|
||||
import { ConnectToPGSqlDialog } from '../ui/dialogs/connectPGDialog';
|
||||
import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
||||
import { ControllerModel, Registration } from './controllerModel';
|
||||
import { parseIpAndPort } from '../common/utils';
|
||||
import { UserCancelledError } from '../common/api';
|
||||
import { ResourceModel } from './resourceModel';
|
||||
import { Deferred } from '../common/promise';
|
||||
import { parseIpAndPort } from '../common/utils';
|
||||
|
||||
export type EngineSettingsModel = {
|
||||
parameterName: string | undefined,
|
||||
value: string | undefined,
|
||||
description: string | undefined,
|
||||
min: string | undefined,
|
||||
max: string | undefined,
|
||||
options: string | undefined,
|
||||
type: string | undefined
|
||||
};
|
||||
|
||||
export class PostgresModel extends ResourceModel {
|
||||
private _config?: azdataExt.PostgresServerShowResult;
|
||||
public _engineSettings: EngineSettingsModel[] = [];
|
||||
private readonly _azdataApi: azdataExt.IExtension;
|
||||
|
||||
private readonly _onConfigUpdated = new vscode.EventEmitter<azdataExt.PostgresServerShowResult>();
|
||||
public readonly _onEngineSettingsUpdated = new vscode.EventEmitter<EngineSettingsModel[]>();
|
||||
public onConfigUpdated = this._onConfigUpdated.event;
|
||||
public onEngineSettingsUpdated = this._onEngineSettingsUpdated.event;
|
||||
public configLastUpdated?: Date;
|
||||
public engineSettingsLastUpdated?: Date;
|
||||
|
||||
private _refreshPromise?: Deferred<void>;
|
||||
|
||||
constructor(controllerModel: ControllerModel, info: ResourceInfo, registration: Registration) {
|
||||
super(controllerModel, info, registration);
|
||||
constructor(_controllerModel: ControllerModel, private _pgInfo: PGResourceInfo, registration: Registration, private _treeDataProvider: AzureArcTreeDataProvider) {
|
||||
super(_controllerModel, _pgInfo, registration);
|
||||
this._azdataApi = <azdataExt.IExtension>vscode.extensions.getExtension(azdataExt.extension.name)?.exports;
|
||||
}
|
||||
|
||||
@@ -103,4 +121,84 @@ export class PostgresModel extends ResourceModel {
|
||||
this._refreshPromise = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
public async getEngineSettings(): Promise<void> {
|
||||
if (!this._connectionProfile) {
|
||||
await this.getConnectionProfile();
|
||||
}
|
||||
|
||||
// We haven't connected yet so do so now and then store the ID for the active connection
|
||||
if (!this._activeConnectionId) {
|
||||
const result = await azdata.connection.connect(this._connectionProfile!, false, false);
|
||||
if (!result.connected) {
|
||||
throw new Error(result.errorMessage);
|
||||
}
|
||||
this._activeConnectionId = result.connectionId;
|
||||
}
|
||||
|
||||
const provider = azdata.dataprotocol.getProvider<azdata.QueryProvider>(this._connectionProfile!.providerName, azdata.DataProviderType.QueryProvider);
|
||||
const ownerUri = await azdata.connection.getUriForConnection(this._activeConnectionId);
|
||||
|
||||
const engineSettings = await provider.runQueryAndReturn(ownerUri, 'select name, setting, short_desc,min_val, max_val, enumvals, vartype from pg_settings');
|
||||
if (!engineSettings) {
|
||||
throw new Error('Could not fetch engine settings');
|
||||
}
|
||||
engineSettings.rows.forEach(row => {
|
||||
let rowValues = row.map(c => c.displayValue);
|
||||
let result: EngineSettingsModel = {
|
||||
parameterName: rowValues.shift(),
|
||||
value: rowValues.shift(),
|
||||
description: rowValues.shift(),
|
||||
min: rowValues.shift(),
|
||||
max: rowValues.shift(),
|
||||
options: rowValues.shift(),
|
||||
type: rowValues.shift()
|
||||
};
|
||||
|
||||
this._engineSettings.push(result);
|
||||
});
|
||||
|
||||
this.engineSettingsLastUpdated = new Date();
|
||||
}
|
||||
|
||||
protected createConnectionProfile(): azdata.IConnectionProfile {
|
||||
const ipAndPort = parseIpAndPort(this.config?.status.externalEndpoint || '');
|
||||
return {
|
||||
serverName: `${ipAndPort.ip},${ipAndPort.port}`,
|
||||
databaseName: '',
|
||||
authenticationType: 'SqlLogin',
|
||||
providerName: loc.postgresProviderName,
|
||||
connectionName: '',
|
||||
userName: this._pgInfo.userName || '',
|
||||
password: '',
|
||||
savePassword: true,
|
||||
groupFullName: undefined,
|
||||
saveProfile: true,
|
||||
id: '',
|
||||
groupId: undefined,
|
||||
options: {
|
||||
host: `${ipAndPort.ip}`,
|
||||
port: `${ipAndPort.port}`,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected async promptForConnection(connectionProfile: azdata.IConnectionProfile): Promise<void> {
|
||||
const connectToSqlDialog = new ConnectToPGSqlDialog(this.controllerModel, this);
|
||||
connectToSqlDialog.showDialog(loc.connectToPGSql(this.info.name), connectionProfile);
|
||||
let profileFromDialog = await connectToSqlDialog.waitForClose();
|
||||
|
||||
if (profileFromDialog) {
|
||||
this.updateConnectionProfile(profileFromDialog);
|
||||
} else {
|
||||
throw new UserCancelledError();
|
||||
}
|
||||
}
|
||||
|
||||
protected async updateConnectionProfile(connectionProfile: azdata.IConnectionProfile): Promise<void> {
|
||||
this._connectionProfile = connectionProfile;
|
||||
this.info.connectionId = connectionProfile.id;
|
||||
this._pgInfo.userName = connectionProfile.userName;
|
||||
await this._treeDataProvider.saveControllers();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,22 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ResourceInfo } from 'arc';
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import { ControllerModel, Registration } from './controllerModel';
|
||||
import { createCredentialId } from '../common/utils';
|
||||
import { credentialNamespace } from '../constants';
|
||||
|
||||
export abstract class ResourceModel {
|
||||
|
||||
private readonly _onRegistrationUpdated = new vscode.EventEmitter<Registration>();
|
||||
public onRegistrationUpdated = this._onRegistrationUpdated.event;
|
||||
|
||||
// The saved connection information
|
||||
protected _connectionProfile: azdata.IConnectionProfile | undefined = undefined;
|
||||
// The ID of the active connection used to query the server
|
||||
protected _activeConnectionId: string | undefined = undefined;
|
||||
|
||||
constructor(public readonly controllerModel: ControllerModel, public info: ResourceInfo, private _registration: Registration) { }
|
||||
|
||||
public get registration(): Registration {
|
||||
@@ -23,5 +31,48 @@ export abstract class ResourceModel {
|
||||
this._onRegistrationUpdated.fire(this._registration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the saved connection profile associated with this model. Will prompt for one if
|
||||
* we don't have one or can't find it (it was deleted)
|
||||
*/
|
||||
protected async getConnectionProfile(): Promise<void> {
|
||||
let connectionProfile: azdata.IConnectionProfile | undefined = this.createConnectionProfile();
|
||||
|
||||
// If we have the ID stored then try to retrieve the password from previous connections
|
||||
if (this.info.connectionId) {
|
||||
try {
|
||||
const credentialProvider = await azdata.credentials.getProvider(credentialNamespace);
|
||||
const credentials = await credentialProvider.readCredential(createCredentialId(this.controllerModel.info.id, this.info.resourceType, this.info.name));
|
||||
if (credentials.password) {
|
||||
// Try to connect to verify credentials are still valid
|
||||
connectionProfile.password = credentials.password;
|
||||
// If we don't have a username for some reason then just continue on and we'll prompt for the username below
|
||||
if (connectionProfile.userName) {
|
||||
const result = await azdata.connection.connect(connectionProfile, false, false);
|
||||
if (!result.connected) {
|
||||
await this.promptForConnection(connectionProfile);
|
||||
} else {
|
||||
this.updateConnectionProfile(connectionProfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`Unexpected error fetching password for instance ${err}`);
|
||||
// ignore - something happened fetching the password so just reprompt
|
||||
}
|
||||
}
|
||||
|
||||
if (!connectionProfile?.userName || !connectionProfile?.password) {
|
||||
// Need to prompt user for password since we don't have one stored
|
||||
await this.promptForConnection(connectionProfile);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract refresh(): Promise<void>;
|
||||
|
||||
protected abstract createConnectionProfile(): azdata.IConnectionProfile;
|
||||
|
||||
protected abstract promptForConnection(connectionProfile: azdata.IConnectionProfile): Promise<void>;
|
||||
|
||||
protected abstract updateConnectionProfile(connectionProfile: azdata.IConnectionProfile): Promise<void>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user