mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Postgres Parameters Page (#13855)
* 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 * Git errors * Git errors fix * Fixing Css * Freezes, Search function working, 20 parameters * Fixed re * Git errors * Save and reset commands working * Discard works, updated how engine settings refresh with model * Updated search, add back loading for when trying to connect * Cleaning up comments left in code * Git error * Corrected names of icons and constants, Fixed Miaa dialog title * Removed using any on page, added void return types, took out commented code * 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 * Fixed a comment * Added loading component for waiting for postgres extension to be installed * Fix parameters page to show parameters if engine settings are already loaded (#13996) * Added progress message for installing postgres extension * Minor styling updates * Making sure search box and rest buttons are enabled when opening page with loaded data. Update refresh * Git errors * change name * Code review updates: Combined create parameters and refresh table. * Update sql-assessment to use latest ads-extension-telemetry npm package (#14003) * Change configure Jupyter server steps from async to sync (#13937) * change config steps to sync * fix tests * use pathexistsSync * remove pathExistsSync call * address PR comments * Use a minimum cell height to prevent whitespace markdown cells from becoming invisible. (#14008) * Fix validation errors (#14009) * Fix validation errors * fix compile * update return type * Cleanup model component wrapper event handlers (#14012) * Clean up button component disposables (#14011) * Clean up button component disposables * consolidate logic * Adding Dacpac extension telemetry and core wizard/page telemetry updates(#13859) * Dacpac telmetry code changes * Removed added spaces * Generate deployScript accessibility changed back to public * code review suggessions updates * dacpac extension tests fixes * Updated time and filesize methods allowing general return values * Telemetry code updates * Dacpac Telemetry potential data loss capture and PII error excluded * Dacpac telemetry code updates for comments * Wizard pages navigation telemetry event capture moved to the core * DacpacTelemetry code updates * Extension wizard cancel telemetry for data loss * Dacpac telemetry pagename and small code updates * final Dacpac telemetry code updates... * migrated loc files (#14015) * Took out some info bubbles and addingitems * Update search function * Handle special value occasions * Undo change * Undo change Co-authored-by: chgagnon <chgagnon@microsoft.com> Co-authored-by: Lucy Zhang <luczhan@microsoft.com> Co-authored-by: Cory Rivera <corivera@microsoft.com> Co-authored-by: Sai Avishkar Sreerama <74571829+ssreerama@users.noreply.github.com> Co-authored-by: khoiph1 <khoiph@microsoft.com>
This commit is contained in:
@@ -30,6 +30,8 @@ export const newSupportRequest = localize('arc.newSupportRequest', "New support
|
|||||||
export const diagnoseAndSolveProblems = localize('arc.diagnoseAndSolveProblems', "Diagnose and solve problems");
|
export const diagnoseAndSolveProblems = localize('arc.diagnoseAndSolveProblems', "Diagnose and solve problems");
|
||||||
export const supportAndTroubleshooting = localize('arc.supportAndTroubleshooting', "Support + troubleshooting");
|
export const supportAndTroubleshooting = localize('arc.supportAndTroubleshooting', "Support + troubleshooting");
|
||||||
export const resourceHealth = localize('arc.resourceHealth', "Resource health");
|
export const resourceHealth = localize('arc.resourceHealth', "Resource health");
|
||||||
|
export const parameterName = localize('arc.parameterName', "Parameter Name");
|
||||||
|
export const value = localize('arc.value', "Value");
|
||||||
|
|
||||||
export const newInstance = localize('arc.createNew', "New Instance");
|
export const newInstance = localize('arc.createNew', "New Instance");
|
||||||
export const deleteText = localize('arc.delete', "Delete");
|
export const deleteText = localize('arc.delete', "Delete");
|
||||||
@@ -173,6 +175,8 @@ export function rangeSetting(min: string, max: string): string { return localize
|
|||||||
export function allowedValue(value: string): string { return localize('arc.allowedValue', "Value is expected to be {0}", value); }
|
export function allowedValue(value: string): string { return localize('arc.allowedValue', "Value is expected to be {0}", value); }
|
||||||
export function databaseCreated(name: string): string { return localize('arc.databaseCreated', "Database {0} created", name); }
|
export function databaseCreated(name: string): string { return localize('arc.databaseCreated', "Database {0} created", name); }
|
||||||
export function deletingInstance(name: string): string { return localize('arc.deletingInstance', "Deleting instance '{0}'...", name); }
|
export function deletingInstance(name: string): string { return localize('arc.deletingInstance', "Deleting instance '{0}'...", name); }
|
||||||
|
export function installingExtension(name: string): string { return localize('arc.installingExtension', "Installing extension '{0}'...", name); }
|
||||||
|
export function extensionInstalled(name: string): string { return localize('arc.extensionInstalled', "Extension '{0}' has been installed.", name); }
|
||||||
export function updatingInstance(name: string): string { return localize('arc.updatingInstance', "Updating instance '{0}'...", name); }
|
export function updatingInstance(name: string): string { return localize('arc.updatingInstance', "Updating instance '{0}'...", name); }
|
||||||
export function instanceDeleted(name: string): string { return localize('arc.instanceDeleted', "Instance '{0}' deleted", name); }
|
export function instanceDeleted(name: string): string { return localize('arc.instanceDeleted', "Instance '{0}' deleted", name); }
|
||||||
export function instanceUpdated(name: string): string { return localize('arc.instanceUpdated', "Instance '{0}' updated", name); }
|
export function instanceUpdated(name: string): string { return localize('arc.instanceUpdated', "Instance '{0}' updated", name); }
|
||||||
@@ -198,6 +202,7 @@ export const pgConnectionRequired = localize('arc.pgConnectionRequired', "A conn
|
|||||||
export const couldNotFindControllerRegistration = localize('arc.couldNotFindControllerRegistration', "Could not find controller registration.");
|
export const couldNotFindControllerRegistration = localize('arc.couldNotFindControllerRegistration', "Could not find controller registration.");
|
||||||
export function outOfRange(min: string, max: string): string { return localize('arc.outOfRange', "The number must be in range {0} - {1}", min, max); }
|
export function outOfRange(min: string, max: string): string { return localize('arc.outOfRange', "The number must be in range {0} - {1}", min, max); }
|
||||||
export function refreshFailed(error: any): string { return localize('arc.refreshFailed', "Refresh failed. {0}", getErrorMessage(error)); }
|
export function refreshFailed(error: any): string { return localize('arc.refreshFailed', "Refresh failed. {0}", getErrorMessage(error)); }
|
||||||
|
export function resetFailed(error: any): string { return localize('arc.resetFailed', "Reset failed. {0}", getErrorMessage(error)); }
|
||||||
export function openDashboardFailed(error: any): string { return localize('arc.openDashboardFailed', "Error opening dashboard. {0}", getErrorMessage(error)); }
|
export function openDashboardFailed(error: any): string { return localize('arc.openDashboardFailed', "Error opening dashboard. {0}", getErrorMessage(error)); }
|
||||||
export function instanceDeletionFailed(name: string, error: any): string { return localize('arc.instanceDeletionFailed', "Failed to delete instance {0}. {1}", name, getErrorMessage(error)); }
|
export function instanceDeletionFailed(name: string, error: any): string { return localize('arc.instanceDeletionFailed', "Failed to delete instance {0}. {1}", name, getErrorMessage(error)); }
|
||||||
export function instanceUpdateFailed(name: string, error: any): string { return localize('arc.instanceUpdateFailed', "Failed to update instance {0}. {1}", name, getErrorMessage(error)); }
|
export function instanceUpdateFailed(name: string, error: any): string { return localize('arc.instanceUpdateFailed', "Failed to update instance {0}. {1}", name, getErrorMessage(error)); }
|
||||||
@@ -207,6 +212,7 @@ export function connectToControllerFailed(url: string, error: any): string { ret
|
|||||||
export function connectToMSSqlFailed(serverName: string, error: any): string { return localize('arc.connectToMSSqlFailed', "Could not connect to SQL managed instance - Azure Arc Instance {0}. {1}", serverName, getErrorMessage(error)); }
|
export function connectToMSSqlFailed(serverName: string, error: any): string { return localize('arc.connectToMSSqlFailed', "Could not connect to SQL managed instance - Azure Arc Instance {0}. {1}", serverName, getErrorMessage(error)); }
|
||||||
export function connectToPGSqlFailed(serverName: string, error: any): string { return localize('arc.connectToPGSqlFailed', "Could not connect to PostgreSQL Hyperscale - Azure Arc Instance {0}. {1}", serverName, getErrorMessage(error)); }
|
export function connectToPGSqlFailed(serverName: string, error: any): string { return localize('arc.connectToPGSqlFailed', "Could not connect to PostgreSQL Hyperscale - Azure Arc Instance {0}. {1}", serverName, getErrorMessage(error)); }
|
||||||
export function missingExtension(extensionName: string): string { return localize('arc.missingExtension', "The {0} extension is required to view engine settings. Do you wish to install it now?", extensionName); }
|
export function missingExtension(extensionName: string): string { return localize('arc.missingExtension', "The {0} extension is required to view engine settings. Do you wish to install it now?", extensionName); }
|
||||||
|
export function extensionInstallationFailed(extensionName: string): string { return localize('arc.extensionInstallationFailed', "Failed to install extension {0}.", extensionName); }
|
||||||
export function fetchConfigFailed(name: string, error: any): string { return localize('arc.fetchConfigFailed', "An unexpected error occurred retrieving the config for '{0}'. {1}", name, getErrorMessage(error)); }
|
export function fetchConfigFailed(name: string, error: any): string { return localize('arc.fetchConfigFailed', "An unexpected error occurred retrieving the config for '{0}'. {1}", name, getErrorMessage(error)); }
|
||||||
export function fetchEndpointsFailed(name: string, error: any): string { return localize('arc.fetchEndpointsFailed', "An unexpected error occurred retrieving the endpoints for '{0}'. {1}", name, getErrorMessage(error)); }
|
export function fetchEndpointsFailed(name: string, error: any): string { return localize('arc.fetchEndpointsFailed', "An unexpected error occurred retrieving the endpoints for '{0}'. {1}", name, getErrorMessage(error)); }
|
||||||
export function fetchRegistrationsFailed(name: string, error: any): string { return localize('arc.fetchRegistrationsFailed', "An unexpected error occurred retrieving the registrations for '{0}'. {1}", name, getErrorMessage(error)); }
|
export function fetchRegistrationsFailed(name: string, error: any): string { return localize('arc.fetchRegistrationsFailed', "An unexpected error occurred retrieving the registrations for '{0}'. {1}", name, getErrorMessage(error)); }
|
||||||
|
|||||||
@@ -144,22 +144,34 @@ export class PostgresModel extends ResourceModel {
|
|||||||
if (!engineSettings) {
|
if (!engineSettings) {
|
||||||
throw new Error('Could not fetch engine settings');
|
throw new Error('Could not fetch engine settings');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const skippedEngineSettings: String[] = [
|
||||||
|
'archive_command', 'archive_timeout', 'log_directory', 'log_file_mode', 'log_filename', 'restore_command',
|
||||||
|
'shared_preload_libraries', 'synchronous_commit', 'ssl', 'unix_socket_permissions', 'wal_level'
|
||||||
|
];
|
||||||
|
|
||||||
|
this._engineSettings = [];
|
||||||
|
|
||||||
engineSettings.rows.forEach(row => {
|
engineSettings.rows.forEach(row => {
|
||||||
let rowValues = row.map(c => c.displayValue);
|
let rowValues = row.map(c => c.displayValue);
|
||||||
let result: EngineSettingsModel = {
|
let name = rowValues.shift();
|
||||||
parameterName: rowValues.shift(),
|
if (!skippedEngineSettings.includes(name!)) {
|
||||||
value: rowValues.shift(),
|
let result: EngineSettingsModel = {
|
||||||
description: rowValues.shift(),
|
parameterName: name,
|
||||||
min: rowValues.shift(),
|
value: rowValues.shift(),
|
||||||
max: rowValues.shift(),
|
description: rowValues.shift(),
|
||||||
options: rowValues.shift(),
|
min: rowValues.shift(),
|
||||||
type: rowValues.shift()
|
max: rowValues.shift(),
|
||||||
};
|
options: rowValues.shift(),
|
||||||
|
type: rowValues.shift()
|
||||||
|
};
|
||||||
|
|
||||||
this._engineSettings.push(result);
|
this._engineSettings.push(result);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.engineSettingsLastUpdated = new Date();
|
this.engineSettingsLastUpdated = new Date();
|
||||||
|
this._onEngineSettingsUpdated.fire(this._engineSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createConnectionProfile(): azdata.IConnectionProfile {
|
protected createConnectionProfile(): azdata.IConnectionProfile {
|
||||||
|
|||||||
@@ -341,8 +341,8 @@ export class PostgresComputeAndStoragePage extends DashboardPage {
|
|||||||
const information = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const information = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
||||||
iconPath: IconPathHelper.information,
|
iconPath: IconPathHelper.information,
|
||||||
title: loc.workerNodesInformation,
|
title: loc.workerNodesInformation,
|
||||||
width: '12px',
|
width: '15px',
|
||||||
height: '12px',
|
height: '15px',
|
||||||
enabled: false
|
enabled: false
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
@@ -434,8 +434,8 @@ export class PostgresComputeAndStoragePage extends DashboardPage {
|
|||||||
const information = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const information = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
||||||
iconPath: IconPathHelper.information,
|
iconPath: IconPathHelper.information,
|
||||||
title: loc.postgresConfigurationInformation,
|
title: loc.postgresConfigurationInformation,
|
||||||
width: '12px',
|
width: '15px',
|
||||||
height: '12px',
|
height: '15px',
|
||||||
enabled: false
|
enabled: false
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ export class PostgresDashboard extends Dashboard {
|
|||||||
const computeAndStoragePage = new PostgresComputeAndStoragePage(modelView, this._postgresModel);
|
const computeAndStoragePage = new PostgresComputeAndStoragePage(modelView, this._postgresModel);
|
||||||
// TODO: Removed properties page while investigating bug where refreshed values don't appear in UI
|
// TODO: Removed properties page while investigating bug where refreshed values don't appear in UI
|
||||||
// const propertiesPage = new PostgresPropertiesPage(modelView, this._controllerModel, this._postgresModel);
|
// const propertiesPage = new PostgresPropertiesPage(modelView, this._controllerModel, this._postgresModel);
|
||||||
|
// TODO: Removed parameters page while investigating bug where UI freezes dealing with large declarative table of components
|
||||||
|
// const parametersPage = new PostgresParametersPage(modelView, this._postgresModel);
|
||||||
const diagnoseAndSolveProblemsPage = new PostgresDiagnoseAndSolveProblemsPage(modelView, this._context, this._postgresModel);
|
const diagnoseAndSolveProblemsPage = new PostgresDiagnoseAndSolveProblemsPage(modelView, this._context, this._postgresModel);
|
||||||
const supportRequestPage = new PostgresSupportRequestPage(modelView, this._controllerModel, this._postgresModel);
|
const supportRequestPage = new PostgresSupportRequestPage(modelView, this._controllerModel, this._postgresModel);
|
||||||
|
|
||||||
|
|||||||
@@ -10,21 +10,28 @@ import * as loc from '../../../localizedConstants';
|
|||||||
import { UserCancelledError } from '../../../common/api';
|
import { UserCancelledError } from '../../../common/api';
|
||||||
import { IconPathHelper, cssStyles } from '../../../constants';
|
import { IconPathHelper, cssStyles } from '../../../constants';
|
||||||
import { DashboardPage } from '../../components/dashboardPage';
|
import { DashboardPage } from '../../components/dashboardPage';
|
||||||
import { PostgresModel } from '../../../models/postgresModel';
|
import { EngineSettingsModel, PostgresModel } from '../../../models/postgresModel';
|
||||||
|
|
||||||
|
export type ParametersModel = {
|
||||||
|
parameterName: string,
|
||||||
|
valueContainer: azdata.FlexContainer,
|
||||||
|
description: string,
|
||||||
|
resetButton: azdata.ButtonComponent
|
||||||
|
};
|
||||||
|
|
||||||
export class PostgresParametersPage extends DashboardPage {
|
export class PostgresParametersPage extends DashboardPage {
|
||||||
private searchBox?: azdata.InputBoxComponent;
|
private searchBox!: azdata.InputBoxComponent;
|
||||||
private parametersTable!: azdata.DeclarativeTableComponent;
|
private parametersTable!: azdata.DeclarativeTableComponent;
|
||||||
private parameterContainer?: azdata.DivContainer;
|
private parameterContainer?: azdata.DivContainer;
|
||||||
private _parametersTableLoading!: azdata.LoadingComponent;
|
private _parametersTableLoading!: azdata.LoadingComponent;
|
||||||
|
|
||||||
private discardButton?: azdata.ButtonComponent;
|
private discardButton!: azdata.ButtonComponent;
|
||||||
private saveButton?: azdata.ButtonComponent;
|
private saveButton!: azdata.ButtonComponent;
|
||||||
private resetButton?: azdata.ButtonComponent;
|
private resetAllButton!: azdata.ButtonComponent;
|
||||||
private connectToServerButton?: azdata.ButtonComponent;
|
private connectToServerButton?: azdata.ButtonComponent;
|
||||||
|
|
||||||
private engineSettings = `'`;
|
private _parameters: ParametersModel[] = [];
|
||||||
private engineSettingUpdates?: Map<string, string>;
|
private parameterUpdates: Map<string, string> = new Map();
|
||||||
|
|
||||||
private readonly _azdataApi: azdataExt.IExtension;
|
private readonly _azdataApi: azdataExt.IExtension;
|
||||||
|
|
||||||
@@ -35,11 +42,10 @@ export class PostgresParametersPage extends DashboardPage {
|
|||||||
this.initializeConnectButton();
|
this.initializeConnectButton();
|
||||||
this.initializeSearchBox();
|
this.initializeSearchBox();
|
||||||
|
|
||||||
this.engineSettingUpdates = new Map();
|
|
||||||
|
|
||||||
this.disposables.push(
|
this.disposables.push(
|
||||||
this._postgresModel.onConfigUpdated(() => this.eventuallyRunOnInitialized(() => this.handleServiceUpdated())),
|
this._postgresModel.onConfigUpdated(() => this.eventuallyRunOnInitialized(() => this.handleServiceUpdated())),
|
||||||
this._postgresModel.onEngineSettingsUpdated(() => this.eventuallyRunOnInitialized(() => this.handleEngineSettingsUpdated())));
|
this._postgresModel.onEngineSettingsUpdated(() => this.eventuallyRunOnInitialized(() => this.refreshParametersTable()))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected get title(): string {
|
protected get title(): string {
|
||||||
@@ -64,20 +70,15 @@ export class PostgresParametersPage extends DashboardPage {
|
|||||||
CSSStyles: { ...cssStyles.title }
|
CSSStyles: { ...cssStyles.title }
|
||||||
}).component());
|
}).component());
|
||||||
|
|
||||||
const info = this.modelView.modelBuilder.text().withProps({
|
content.addItem(this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.nodeParametersDescription,
|
value: loc.nodeParametersDescription,
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
||||||
}).component();
|
}).component());
|
||||||
|
|
||||||
const link = this.modelView.modelBuilder.hyperlink().withProps({
|
content.addItem(this.modelView.modelBuilder.hyperlink().withProps({
|
||||||
label: loc.learnAboutNodeParameters,
|
label: loc.learnAboutNodeParameters,
|
||||||
url: 'https://docs.microsoft.com/azure/azure-arc/data/configure-server-parameters-postgresql-hyperscale',
|
url: 'https://docs.microsoft.com/azure/azure-arc/data/configure-server-parameters-postgresql-hyperscale'
|
||||||
}).component();
|
}).component(), { CSSStyles: { 'margin-bottom': '20px' } });
|
||||||
|
|
||||||
const infoAndLink = this.modelView.modelBuilder.flexContainer().withLayout({ flexWrap: 'wrap' }).component();
|
|
||||||
infoAndLink.addItem(info, { CSSStyles: { 'margin-right': '5px' } });
|
|
||||||
infoAndLink.addItem(link);
|
|
||||||
content.addItem(infoAndLink, { CSSStyles: { 'margin-bottom': '20px' } });
|
|
||||||
|
|
||||||
content.addItem(this.searchBox!, { CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px', 'margin-bottom': '20px' } });
|
content.addItem(this.searchBox!, { CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px', 'margin-bottom': '20px' } });
|
||||||
|
|
||||||
@@ -85,43 +86,33 @@ export class PostgresParametersPage extends DashboardPage {
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
displayName: 'Parameter Name',
|
displayName: loc.parameterName,
|
||||||
valueType: azdata.DeclarativeDataType.component,
|
valueType: azdata.DeclarativeDataType.string,
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
width: '20%',
|
width: '20%',
|
||||||
headerCssStyles: cssStyles.tableHeader,
|
headerCssStyles: cssStyles.tableHeader,
|
||||||
rowCssStyles: cssStyles.tableRow
|
rowCssStyles: cssStyles.tableRow
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Value',
|
displayName: loc.value,
|
||||||
valueType: azdata.DeclarativeDataType.component,
|
valueType: azdata.DeclarativeDataType.component,
|
||||||
isReadOnly: false,
|
isReadOnly: false,
|
||||||
width: '20%',
|
width: '20%',
|
||||||
headerCssStyles: cssStyles.tableHeader,
|
headerCssStyles: cssStyles.tableHeader,
|
||||||
rowCssStyles: {
|
rowCssStyles: cssStyles.tableRow
|
||||||
...cssStyles.tableRow,
|
|
||||||
'overflow': 'hidden',
|
|
||||||
'text-overflow': 'ellipsis',
|
|
||||||
'white-space': 'nowrap',
|
|
||||||
'max-width': '0'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Description',
|
displayName: loc.description,
|
||||||
valueType: azdata.DeclarativeDataType.component,
|
valueType: azdata.DeclarativeDataType.string,
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
width: '50%',
|
width: '50%',
|
||||||
headerCssStyles: cssStyles.tableHeader,
|
headerCssStyles: cssStyles.tableHeader,
|
||||||
rowCssStyles: {
|
rowCssStyles: {
|
||||||
...cssStyles.tableRow,
|
...cssStyles.tableRow
|
||||||
'overflow': 'hidden',
|
|
||||||
'text-overflow': 'ellipsis',
|
|
||||||
'white-space': 'nowrap',
|
|
||||||
'max-width': '0'
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Reset To Default',
|
displayName: loc.resetToDefault,
|
||||||
valueType: azdata.DeclarativeDataType.component,
|
valueType: azdata.DeclarativeDataType.component,
|
||||||
isReadOnly: false,
|
isReadOnly: false,
|
||||||
width: '10%',
|
width: '10%',
|
||||||
@@ -129,11 +120,7 @@ export class PostgresParametersPage extends DashboardPage {
|
|||||||
rowCssStyles: cssStyles.tableRow
|
rowCssStyles: cssStyles.tableRow
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
data: [
|
data: []
|
||||||
this.parameterComponents('TEST NAME', 'string'),
|
|
||||||
this.parameterComponents('TEST NAME 2', 'real'),
|
|
||||||
this.createParametersTable()]
|
|
||||||
|
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this._parametersTableLoading = this.modelView.modelBuilder.loadingComponent().component();
|
this._parametersTableLoading = this.modelView.modelBuilder.loadingComponent().component();
|
||||||
@@ -156,8 +143,10 @@ export class PostgresParametersPage extends DashboardPage {
|
|||||||
enabled: false
|
enabled: false
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
|
let engineSettings: string[] = [];
|
||||||
this.disposables.push(
|
this.disposables.push(
|
||||||
this.saveButton.onDidClick(async () => {
|
this.saveButton.onDidClick(async () => {
|
||||||
|
this.saveButton!.enabled = false;
|
||||||
try {
|
try {
|
||||||
await vscode.window.withProgress(
|
await vscode.window.withProgress(
|
||||||
{
|
{
|
||||||
@@ -166,20 +155,19 @@ export class PostgresParametersPage extends DashboardPage {
|
|||||||
cancellable: false
|
cancellable: false
|
||||||
},
|
},
|
||||||
async (_progress, _token): Promise<void> => {
|
async (_progress, _token): Promise<void> => {
|
||||||
//Edit multiple
|
|
||||||
// azdata arc postgres server edit -n <server group name> -e '<parameter name>=<parameter value>, <parameter name>=<parameter value>,...'
|
|
||||||
try {
|
try {
|
||||||
this.engineSettingUpdates!.forEach((value: string) => {
|
this.parameterUpdates!.forEach((value, key) => {
|
||||||
this.engineSettings += value + ', ';
|
engineSettings.push(`${key}="${value}"`);
|
||||||
});
|
});
|
||||||
const session = await this._postgresModel.controllerModel.acquireAzdataSession();
|
const session = await this._postgresModel.controllerModel.acquireAzdataSession();
|
||||||
try {
|
try {
|
||||||
await this._azdataApi.azdata.arc.postgres.server.edit(
|
await this._azdataApi.azdata.arc.postgres.server.edit(
|
||||||
this._postgresModel.info.name, { engineSettings: this.engineSettings + `'` });
|
this._postgresModel.info.name,
|
||||||
|
{ engineSettings: engineSettings.toString() },
|
||||||
|
this._postgresModel.engineVersion);
|
||||||
} finally {
|
} finally {
|
||||||
session.dispose();
|
session.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// If an error occurs while editing the instance then re-enable the save button since
|
// If an error occurs while editing the instance then re-enable the save button since
|
||||||
// the edit wasn't successfully applied
|
// the edit wasn't successfully applied
|
||||||
@@ -192,14 +180,16 @@ export class PostgresParametersPage extends DashboardPage {
|
|||||||
|
|
||||||
vscode.window.showInformationMessage(loc.instanceUpdated(this._postgresModel.info.name));
|
vscode.window.showInformationMessage(loc.instanceUpdated(this._postgresModel.info.name));
|
||||||
|
|
||||||
this.engineSettings = `'`;
|
engineSettings = [];
|
||||||
this.engineSettingUpdates!.clear();
|
this.parameterUpdates!.clear();
|
||||||
this.discardButton!.enabled = false;
|
this.discardButton!.enabled = false;
|
||||||
|
this.resetAllButton!.enabled = true;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
vscode.window.showErrorMessage(loc.instanceUpdateFailed(this._postgresModel.info.name, error));
|
vscode.window.showErrorMessage(loc.instanceUpdateFailed(this._postgresModel.info.name, error));
|
||||||
}
|
}
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// Discard
|
// Discard
|
||||||
this.discardButton = this.modelView.modelBuilder.button().withProps({
|
this.discardButton = this.modelView.modelBuilder.button().withProps({
|
||||||
@@ -212,27 +202,27 @@ export class PostgresParametersPage extends DashboardPage {
|
|||||||
this.discardButton.onDidClick(async () => {
|
this.discardButton.onDidClick(async () => {
|
||||||
this.discardButton!.enabled = false;
|
this.discardButton!.enabled = false;
|
||||||
try {
|
try {
|
||||||
// TODO
|
this.refreshParametersTable();
|
||||||
// this.parametersTable.data = [];
|
|
||||||
this.engineSettingUpdates!.clear();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
vscode.window.showErrorMessage(loc.pageDiscardFailed(error));
|
vscode.window.showErrorMessage(loc.pageDiscardFailed(error));
|
||||||
} finally {
|
} finally {
|
||||||
this.saveButton!.enabled = false;
|
this.saveButton!.enabled = false;
|
||||||
}
|
}
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// Reset
|
// Reset all
|
||||||
this.resetButton = this.modelView.modelBuilder.button().withProps({
|
this.resetAllButton = this.modelView.modelBuilder.button().withProps({
|
||||||
label: loc.resetAllToDefault,
|
label: loc.resetAllToDefault,
|
||||||
iconPath: IconPathHelper.reset,
|
iconPath: IconPathHelper.reset,
|
||||||
enabled: true
|
enabled: false
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.disposables.push(
|
this.disposables.push(
|
||||||
this.resetButton.onDidClick(async () => {
|
this.resetAllButton.onDidClick(async () => {
|
||||||
this.resetButton!.enabled = false;
|
this.resetAllButton!.enabled = false;
|
||||||
this.discardButton!.enabled = false;
|
this.discardButton!.enabled = false;
|
||||||
|
this.saveButton!.enabled = false;
|
||||||
try {
|
try {
|
||||||
await vscode.window.withProgress(
|
await vscode.window.withProgress(
|
||||||
{
|
{
|
||||||
@@ -247,11 +237,17 @@ export class PostgresParametersPage extends DashboardPage {
|
|||||||
try {
|
try {
|
||||||
session = await this._postgresModel.controllerModel.acquireAzdataSession();
|
session = await this._postgresModel.controllerModel.acquireAzdataSession();
|
||||||
await this._azdataApi.azdata.arc.postgres.server.edit(
|
await this._azdataApi.azdata.arc.postgres.server.edit(
|
||||||
this._postgresModel.info.name, { engineSettings: `'' -re` });
|
this._postgresModel.info.name,
|
||||||
|
{ engineSettings: `''`, replaceEngineSettings: true },
|
||||||
|
this._postgresModel.engineVersion);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// If an error occurs while resetting the instance then re-enable the reset button since
|
// If an error occurs while resetting the instance then re-enable the reset button since
|
||||||
// the edit wasn't successfully applied
|
// the edit wasn't successfully applied
|
||||||
this.resetButton!.enabled = true;
|
if (this.parameterUpdates.size > 0) {
|
||||||
|
this.discardButton!.enabled = true;
|
||||||
|
this.saveButton!.enabled = true;
|
||||||
|
}
|
||||||
|
this.resetAllButton!.enabled = true;
|
||||||
throw err;
|
throw err;
|
||||||
} finally {
|
} finally {
|
||||||
session?.dispose();
|
session?.dispose();
|
||||||
@@ -260,20 +256,24 @@ export class PostgresParametersPage extends DashboardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
vscode.window.showInformationMessage(loc.instanceUpdated(this._postgresModel.info.name));
|
||||||
|
this.parameterUpdates!.clear();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
vscode.window.showErrorMessage(loc.refreshFailed(error));
|
vscode.window.showErrorMessage(loc.resetFailed(error));
|
||||||
}
|
}
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
|
|
||||||
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([
|
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([
|
||||||
{ component: this.saveButton },
|
{ component: this.saveButton },
|
||||||
{ component: this.discardButton },
|
{ component: this.discardButton },
|
||||||
{ component: this.resetButton }
|
{ component: this.resetAllButton }
|
||||||
]).component();
|
]).component();
|
||||||
}
|
}
|
||||||
|
|
||||||
private initializeConnectButton() {
|
private initializeConnectButton(): void {
|
||||||
|
|
||||||
this.connectToServerButton = this.modelView.modelBuilder.button().withProps({
|
this.connectToServerButton = this.modelView.modelBuilder.button().withProps({
|
||||||
label: loc.connectToServer,
|
label: loc.connectToServer,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@@ -290,211 +290,36 @@ export class PostgresParametersPage extends DashboardPage {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await vscode.commands.executeCommand('workbench.extensions.installExtension', loc.postgresExtension);
|
await vscode.window.withProgress(
|
||||||
|
{
|
||||||
|
location: vscode.ProgressLocation.Notification,
|
||||||
|
title: loc.installingExtension(loc.postgresExtension),
|
||||||
|
cancellable: false
|
||||||
|
},
|
||||||
|
async (_progress, _token): Promise<void> => {
|
||||||
|
try {
|
||||||
|
await vscode.commands.executeCommand('workbench.extensions.installExtension', loc.postgresExtension);
|
||||||
|
} catch (err) {
|
||||||
|
vscode.window.showErrorMessage(loc.extensionInstallationFailed(loc.postgresExtension));
|
||||||
|
this.connectToServerButton!.enabled = true;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
vscode.window.showInformationMessage(loc.extensionInstalled(loc.postgresExtension));
|
||||||
}
|
}
|
||||||
|
|
||||||
await this._postgresModel.getEngineSettings().catch(err => {
|
this._parametersTableLoading!.loading = true;
|
||||||
// If an error occurs show a message so the user knows something failed but still
|
await this.callGetEngineSettings().finally(() => this._parametersTableLoading!.loading = false);
|
||||||
// fire the event so callers can know to update (e.g. so dashboards don't show the
|
this.searchBox!.enabled = true;
|
||||||
// loading icon forever)
|
this.resetAllButton!.enabled = true;
|
||||||
if (err instanceof UserCancelledError) {
|
|
||||||
vscode.window.showWarningMessage(loc.pgConnectionRequired);
|
|
||||||
} else {
|
|
||||||
vscode.window.showErrorMessage(loc.fetchEngineSettingsFailed(this._postgresModel.info.name, err));
|
|
||||||
}
|
|
||||||
this._postgresModel.engineSettingsLastUpdated = new Date();
|
|
||||||
this._postgresModel._onEngineSettingsUpdated.fire(this._postgresModel._engineSettings);
|
|
||||||
this.connectToServerButton!.enabled = true;
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.parameterContainer!.clearItems();
|
this.parameterContainer!.clearItems();
|
||||||
this.parameterContainer!.addItem(this.parametersTable);
|
this.parameterContainer!.addItem(this.parametersTable);
|
||||||
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeSearchBox() {
|
|
||||||
this.searchBox = this.modelView.modelBuilder.inputBox().withProps({
|
|
||||||
readOnly: false,
|
|
||||||
placeHolder: loc.searchToFilter
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this.disposables.push(
|
|
||||||
this.searchBox.onTextChanged(() => {
|
|
||||||
this.filterParameters();
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private filterParameters() {
|
private selectComponent(): void {
|
||||||
//TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
private createParametersTable(): any[] {
|
|
||||||
/*Define server settings that shouldn't be modified. we block archive_*, restore_*, and synchronous_commit to prevent the user
|
|
||||||
from messing up our backups. (we rely on synchronous_commit to ensure WAL changes are written immediately.)
|
|
||||||
we block log_* to protect our logging. we block wal_level because Citus needs a particular wal_Level to rebalance shards
|
|
||||||
TODO: Review list of blacklisted parameters. wal_level should only be blacklisted if sharding is enabled
|
|
||||||
To not be modified
|
|
||||||
"archive_command", "archive_timeout", "log_directory", "log_file_mode", "log_filename", "restore_command",
|
|
||||||
"shared_preload_libraries", "synchronous_commit", "ssl", "unix_socket_permissions", "wal_level" */
|
|
||||||
|
|
||||||
// For ev in this._postgresModel._engineSettings
|
|
||||||
// create row
|
|
||||||
// return rows
|
|
||||||
this.parameterComponents('engineSetting', '');
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
private parameterComponents(name: string, type: string): any[] {
|
|
||||||
let data = [];
|
|
||||||
|
|
||||||
// Set parameter name
|
|
||||||
const parameterName = this.modelView.modelBuilder.text().withProps({
|
|
||||||
value: name,
|
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
|
||||||
}).component();
|
|
||||||
data.push(parameterName);
|
|
||||||
|
|
||||||
// Container to hold input component and information bubble
|
|
||||||
const valueContainer = this.modelView.modelBuilder.flexContainer().withLayout({ alignItems: 'center' }).component();
|
|
||||||
|
|
||||||
// Information bubble title to be set depening on type of input
|
|
||||||
let information = this.modelView.modelBuilder.button().withProps({
|
|
||||||
iconPath: IconPathHelper.information,
|
|
||||||
width: '12px',
|
|
||||||
height: '12px',
|
|
||||||
enabled: false
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
if (type === 'enum') {
|
|
||||||
// If type is enum, component should be drop down menu
|
|
||||||
let valueBox = this.modelView.modelBuilder.dropDown().withProps({
|
|
||||||
values: [], //TODO,
|
|
||||||
value: '', //TODO
|
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
|
||||||
}).component();
|
|
||||||
valueContainer.addItem(valueBox, { CSSStyles: { 'margin-right': '0px', 'margin-bottom': '15px' } });
|
|
||||||
|
|
||||||
this.disposables.push(
|
|
||||||
valueBox.onValueChanged(() => {
|
|
||||||
this.engineSettingUpdates!.set(name, String(valueBox.value));
|
|
||||||
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
information.updateProperty('title', loc.allowedValue('enums')); //TODO
|
|
||||||
} else if (type === 'bool') {
|
|
||||||
// If type is bool, component should be checkbox to turn on or off
|
|
||||||
let valueBox = this.modelView.modelBuilder.checkBox().withProps({
|
|
||||||
label: loc.on,
|
|
||||||
checked: true, //TODO
|
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
|
||||||
}).component();
|
|
||||||
valueContainer.addItem(valueBox, { CSSStyles: { 'margin-right': '0px', 'margin-bottom': '15px' } });
|
|
||||||
|
|
||||||
this.disposables.push(
|
|
||||||
valueBox.onChanged(() => {
|
|
||||||
if (valueBox.checked) {
|
|
||||||
this.engineSettingUpdates!.set(name, loc.on);
|
|
||||||
} else {
|
|
||||||
this.engineSettingUpdates!.set(name, loc.off);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
information.updateProperty('title', loc.allowedValue('on,off')); //TODO
|
|
||||||
} else if (type === 'string') {
|
|
||||||
// If type is string, component should be text inputbox
|
|
||||||
// How to add validation: .withValidation(component => component.value?.search('[0-9]') == -1)
|
|
||||||
let valueBox = this.modelView.modelBuilder.inputBox().withProps({
|
|
||||||
readOnly: false,
|
|
||||||
value: '', //TODO
|
|
||||||
CSSStyles: { 'margin-bottom': '15px', 'min-width': '50px', 'max-width': '200px' }
|
|
||||||
}).component();
|
|
||||||
valueContainer.addItem(valueBox, { CSSStyles: { 'margin-right': '0px', 'margin-bottom': '15px' } });
|
|
||||||
|
|
||||||
this.disposables.push(
|
|
||||||
valueBox.onTextChanged(() => {
|
|
||||||
this.engineSettingUpdates!.set(name, valueBox.value!);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
information.updateProperty('title', loc.allowedValue(loc.allowedValue('[A-Za-z._]+'))); //TODO
|
|
||||||
} else {
|
|
||||||
// If type is real or interger, component should be inputbox set to inputType of number. Max and min values also set.
|
|
||||||
let valueBox = this.modelView.modelBuilder.inputBox().withProps({
|
|
||||||
readOnly: false,
|
|
||||||
min: 0, //TODO
|
|
||||||
max: 10000,
|
|
||||||
validationErrorMessage: loc.outOfRange('min', 'max'), //TODO
|
|
||||||
inputType: 'number',
|
|
||||||
value: '0', //TODO
|
|
||||||
CSSStyles: { 'margin-bottom': '15px', 'min-width': '50px', 'max-width': '200px' }
|
|
||||||
}).component();
|
|
||||||
valueContainer.addItem(valueBox, { CSSStyles: { 'margin-right': '0px', 'margin-bottom': '15px' } });
|
|
||||||
|
|
||||||
this.disposables.push(
|
|
||||||
valueBox.onTextChanged(() => {
|
|
||||||
this.engineSettingUpdates!.set(name, valueBox.value!);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
information.updateProperty('title', loc.allowedValue(loc.rangeSetting('min', 'max'))); //TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
valueContainer.addItem(information, { CSSStyles: { 'margin-left': '5px', 'margin-bottom': '15px' } });
|
|
||||||
data.push(valueContainer);
|
|
||||||
|
|
||||||
const parameterDescription = this.modelView.modelBuilder.text().withProps({
|
|
||||||
value: 'TEST DESCRIPTION HERE ...............................ytgbyugvtyvctyrcvytjv ycrtctyv tyfty ftyuvuyvuy', // TODO
|
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
|
||||||
}).component();
|
|
||||||
data.push(parameterDescription);
|
|
||||||
|
|
||||||
// Can reset individual component
|
|
||||||
const resetParameter = this.modelView.modelBuilder.button().withProps({
|
|
||||||
iconPath: IconPathHelper.reset,
|
|
||||||
title: loc.resetToDefault,
|
|
||||||
width: '20px',
|
|
||||||
height: '20px',
|
|
||||||
enabled: true
|
|
||||||
}).component();
|
|
||||||
data.push(resetParameter);
|
|
||||||
|
|
||||||
// azdata arc postgres server edit -n postgres01 -e shared_buffers=
|
|
||||||
this.disposables.push(
|
|
||||||
resetParameter.onDidClick(async () => {
|
|
||||||
try {
|
|
||||||
await vscode.window.withProgress(
|
|
||||||
{
|
|
||||||
location: vscode.ProgressLocation.Notification,
|
|
||||||
title: loc.updatingInstance(this._postgresModel.info.name),
|
|
||||||
cancellable: false
|
|
||||||
},
|
|
||||||
async (_progress, _token) => {
|
|
||||||
const session = await this._postgresModel.controllerModel.acquireAzdataSession();
|
|
||||||
try {
|
|
||||||
this._azdataApi.azdata.arc.postgres.server.edit(
|
|
||||||
this._postgresModel.info.name, { engineSettings: name + '=' });
|
|
||||||
} finally {
|
|
||||||
session.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
vscode.window.showInformationMessage(loc.instanceUpdated(this._postgresModel.info.name));
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
vscode.window.showErrorMessage(loc.instanceUpdateFailed(this._postgresModel.info.name, error));
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private selectComponent() {
|
|
||||||
if (!this._postgresModel.engineSettingsLastUpdated) {
|
if (!this._postgresModel.engineSettingsLastUpdated) {
|
||||||
this.parameterContainer!.addItem(this.modelView.modelBuilder.text().withProps({
|
this.parameterContainer!.addItem(this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.connectToPostgresDescription,
|
value: loc.connectToPostgresDescription,
|
||||||
@@ -503,19 +328,249 @@ export class PostgresParametersPage extends DashboardPage {
|
|||||||
this.parameterContainer!.addItem(this.connectToServerButton!, { CSSStyles: { 'max-width': '125px' } });
|
this.parameterContainer!.addItem(this.connectToServerButton!, { CSSStyles: { 'max-width': '125px' } });
|
||||||
this.parameterContainer!.addItem(this._parametersTableLoading!);
|
this.parameterContainer!.addItem(this._parametersTableLoading!);
|
||||||
} else {
|
} else {
|
||||||
|
this.searchBox!.enabled = true;
|
||||||
|
this.resetAllButton!.enabled = true;
|
||||||
this.parameterContainer!.addItem(this.parametersTable!);
|
this.parameterContainer!.addItem(this.parametersTable!);
|
||||||
|
this.refreshParametersTable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleEngineSettingsUpdated(): void {
|
private async callGetEngineSettings(): Promise<void> {
|
||||||
//TODO
|
try {
|
||||||
|
await this._postgresModel.getEngineSettings();
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof UserCancelledError) {
|
||||||
|
vscode.window.showWarningMessage(loc.pgConnectionRequired);
|
||||||
|
} else {
|
||||||
|
vscode.window.showErrorMessage(loc.fetchEngineSettingsFailed(this._postgresModel.info.name, error));
|
||||||
|
}
|
||||||
|
this.connectToServerButton!.enabled = true;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleServiceUpdated() {
|
private initializeSearchBox(): void {
|
||||||
// TODO
|
this.searchBox = this.modelView.modelBuilder.inputBox().withProps({
|
||||||
if (this._postgresModel.configLastUpdated) {
|
readOnly: false,
|
||||||
|
enabled: false,
|
||||||
|
placeHolder: loc.searchToFilter
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this.disposables.push(
|
||||||
|
this.searchBox.onTextChanged(() => {
|
||||||
|
if (!this.searchBox!.value) {
|
||||||
|
this.parametersTable.data = this._parameters.map(p => [p.parameterName, p.valueContainer, p.description, p.resetButton]);
|
||||||
|
} else {
|
||||||
|
this.filterParameters(this.searchBox!.value);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private filterParameters(search: string): void {
|
||||||
|
this.parametersTable.data = this._parameters
|
||||||
|
.filter(p => p.parameterName?.search(search) !== -1 || p.description?.search(search) !== -1)
|
||||||
|
.map(p => [p.parameterName, p.valueContainer, p.description, p.resetButton]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleOnTextChanged(component: azdata.InputBoxComponent, currentValue: string | undefined): boolean {
|
||||||
|
if (!component.valid) {
|
||||||
|
// If invalid value retun false and enable discard button
|
||||||
|
this.discardButton!.enabled = true;
|
||||||
|
return false;
|
||||||
|
} else if (component.value === currentValue) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
/* If a valid value has been entered into the input box, enable save and discard buttons
|
||||||
|
so that user could choose to either edit instance or clear all inputs
|
||||||
|
return true */
|
||||||
|
this.saveButton!.enabled = true;
|
||||||
|
this.discardButton!.enabled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private createParameterComponents(engineSetting: EngineSettingsModel): ParametersModel {
|
||||||
|
|
||||||
|
// Container to hold input component and information bubble
|
||||||
|
const valueContainer = this.modelView.modelBuilder.flexContainer().withLayout({ alignItems: 'center' }).component();
|
||||||
|
|
||||||
|
|
||||||
|
if (engineSetting.type === 'enum') {
|
||||||
|
// If type is enum, component should be drop down menu
|
||||||
|
let options = engineSetting.options?.slice(1, -1).split(',');
|
||||||
|
let values: string[] = [];
|
||||||
|
options!.forEach(option => {
|
||||||
|
values.push(option.slice(option.indexOf('"') + 1, -1));
|
||||||
|
});
|
||||||
|
|
||||||
|
let valueBox = this.modelView.modelBuilder.dropDown().withProps({
|
||||||
|
values: values,
|
||||||
|
value: engineSetting.value,
|
||||||
|
width: '150px',
|
||||||
|
CSSStyles: { 'height': '40px' }
|
||||||
|
}).component();
|
||||||
|
valueContainer.addItem(valueBox);
|
||||||
|
|
||||||
|
this.disposables.push(
|
||||||
|
valueBox.onValueChanged(() => {
|
||||||
|
if (engineSetting.value !== String(valueBox.value)) {
|
||||||
|
this.parameterUpdates!.set(engineSetting.parameterName!, String(valueBox.value));
|
||||||
|
this.saveButton!.enabled = true;
|
||||||
|
this.discardButton!.enabled = true;
|
||||||
|
} else if (this.parameterUpdates!.has(engineSetting.parameterName!)) {
|
||||||
|
this.parameterUpdates!.delete(engineSetting.parameterName!);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else if (engineSetting.type === 'bool') {
|
||||||
|
// If type is bool, component should be checkbox to turn on or off
|
||||||
|
let valueBox = this.modelView.modelBuilder.checkBox().withProps({
|
||||||
|
label: loc.on,
|
||||||
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
||||||
|
}).component();
|
||||||
|
valueContainer.addItem(valueBox);
|
||||||
|
|
||||||
|
if (engineSetting.value === 'on') {
|
||||||
|
valueBox.checked = true;
|
||||||
|
} else {
|
||||||
|
valueBox.checked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.disposables.push(
|
||||||
|
valueBox.onChanged(() => {
|
||||||
|
if (valueBox.checked && engineSetting.value === 'off') {
|
||||||
|
this.parameterUpdates!.set(engineSetting.parameterName!, loc.on);
|
||||||
|
this.saveButton!.enabled = true;
|
||||||
|
this.discardButton!.enabled = true;
|
||||||
|
} else if (!valueBox.checked && engineSetting.value === 'on') {
|
||||||
|
this.parameterUpdates!.set(engineSetting.parameterName!, loc.off);
|
||||||
|
this.saveButton!.enabled = true;
|
||||||
|
this.discardButton!.enabled = true;
|
||||||
|
} else if (this.parameterUpdates!.has(engineSetting.parameterName!)) {
|
||||||
|
this.parameterUpdates!.delete(engineSetting.parameterName!);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else if (engineSetting.type === 'string') {
|
||||||
|
// If type is string, component should be text inputbox
|
||||||
|
let valueBox = this.modelView.modelBuilder.inputBox().withProps({
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: engineSetting.value,
|
||||||
|
width: '150px'
|
||||||
|
}).component();
|
||||||
|
valueContainer.addItem(valueBox);
|
||||||
|
|
||||||
|
this.disposables.push(
|
||||||
|
valueBox.onTextChanged(() => {
|
||||||
|
if ((this.handleOnTextChanged(valueBox, engineSetting.value))) {
|
||||||
|
this.parameterUpdates!.set(engineSetting.parameterName!, `"${valueBox.value!}"`);
|
||||||
|
} else if (this.parameterUpdates!.has(engineSetting.parameterName!)) {
|
||||||
|
this.parameterUpdates!.delete(engineSetting.parameterName!);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Child components to be added to container
|
||||||
|
let components: Array<azdata.Component> = [];
|
||||||
|
|
||||||
|
// If type is real or interger, component should be inputbox set to inputType of number. Max and min values also set.
|
||||||
|
let valueBox = this.modelView.modelBuilder.inputBox().withProps({
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
min: parseInt(engineSetting.min!),
|
||||||
|
max: parseInt(engineSetting.max!),
|
||||||
|
validationErrorMessage: loc.outOfRange(engineSetting.min!, engineSetting.max!),
|
||||||
|
inputType: 'number',
|
||||||
|
value: engineSetting.value,
|
||||||
|
width: '150px'
|
||||||
|
}).component();
|
||||||
|
components.push(valueBox);
|
||||||
|
|
||||||
|
this.disposables.push(
|
||||||
|
valueBox.onTextChanged(() => {
|
||||||
|
if ((this.handleOnTextChanged(valueBox, engineSetting.value))) {
|
||||||
|
this.parameterUpdates!.set(engineSetting.parameterName!, valueBox.value!);
|
||||||
|
} else if (this.parameterUpdates!.has(engineSetting.parameterName!)) {
|
||||||
|
this.parameterUpdates!.delete(engineSetting.parameterName!);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Information bubble title to show allowed values
|
||||||
|
let information = this.modelView.modelBuilder.button().withProps({
|
||||||
|
iconPath: IconPathHelper.information,
|
||||||
|
width: '15px',
|
||||||
|
height: '15px',
|
||||||
|
enabled: false
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
information.updateProperty('title', loc.allowedValue(loc.rangeSetting(engineSetting.min!, engineSetting.max!)));
|
||||||
|
components.push(information);
|
||||||
|
valueContainer.addItems(components);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can reset individual parameter
|
||||||
|
const resetParameterButton = this.modelView.modelBuilder.button().withProps({
|
||||||
|
iconPath: IconPathHelper.reset,
|
||||||
|
title: loc.resetToDefault,
|
||||||
|
width: '20px',
|
||||||
|
height: '20px',
|
||||||
|
enabled: true
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this.disposables.push(
|
||||||
|
resetParameterButton.onDidClick(async () => {
|
||||||
|
try {
|
||||||
|
await vscode.window.withProgress(
|
||||||
|
{
|
||||||
|
location: vscode.ProgressLocation.Notification,
|
||||||
|
title: loc.updatingInstance(this._postgresModel.info.name),
|
||||||
|
cancellable: false
|
||||||
|
},
|
||||||
|
async (_progress, _token): Promise<void> => {
|
||||||
|
const session = await this._postgresModel.controllerModel.acquireAzdataSession();
|
||||||
|
try {
|
||||||
|
await this._azdataApi.azdata.arc.postgres.server.edit(
|
||||||
|
this._postgresModel.info.name,
|
||||||
|
{ engineSettings: engineSetting.parameterName + '=' },
|
||||||
|
this._postgresModel.engineVersion);
|
||||||
|
} finally {
|
||||||
|
session.dispose();
|
||||||
|
}
|
||||||
|
await this._postgresModel.refresh();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
vscode.window.showInformationMessage(loc.instanceUpdated(this._postgresModel.info.name));
|
||||||
|
} catch (error) {
|
||||||
|
vscode.window.showErrorMessage(loc.instanceUpdateFailed(this._postgresModel.info.name, error));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let parameter: ParametersModel = {
|
||||||
|
parameterName: engineSetting.parameterName!,
|
||||||
|
valueContainer: valueContainer,
|
||||||
|
description: engineSetting.description!,
|
||||||
|
resetButton: resetParameterButton
|
||||||
|
};
|
||||||
|
|
||||||
|
return parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private refreshParametersTable(): void {
|
||||||
|
this._parameters = this._postgresModel._engineSettings.map(engineSetting => this.createParameterComponents(engineSetting));
|
||||||
|
this.parametersTable.data = this._parameters.map(p => [p.parameterName, p.valueContainer, p.description, p.resetButton]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleServiceUpdated(): Promise<void> {
|
||||||
|
if (this._postgresModel.configLastUpdated && !this._postgresModel.engineSettingsLastUpdated) {
|
||||||
this.connectToServerButton!.enabled = true;
|
this.connectToServerButton!.enabled = true;
|
||||||
this._parametersTableLoading!.loading = false;
|
this._parametersTableLoading!.loading = false;
|
||||||
|
} else if (this._postgresModel.engineSettingsLastUpdated) {
|
||||||
|
await this.callGetEngineSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1097,4 +1097,4 @@
|
|||||||
</Item>
|
</Item>
|
||||||
</Item>
|
</Item>
|
||||||
</Item>
|
</Item>
|
||||||
</LCX>
|
</LCX>
|
||||||
|
|||||||
Reference in New Issue
Block a user