CB-4619 user scope settings (#2471)

* CB-4619 feat: update gql schema

* CB-4808 bulk update user parameters

* CB-4808 param type fix

* CB-4619 feat: updateUserPreferences API


* CB-4619 refactor: settings api

* CB-4619 refactor: move file

* CB-4619 refactor: settings api

* CB-4619 fix: pr review

* CB-4619 refactor: remove web-based product settings

* CB-4619 fix: after pr review

* CB-4619 refactor: move product conf to cloudbeaver conf

* CB-4619 fix: hide settings bar and log exception with changing session language

* CB-4619 fix: revert after merge

* CB-4808 new settings api

* CB-4808 update product.conf format

* CB-4808 update product.conf format

* CB-4619 feat: support server provided settings

* CB-4619 feat: prettify settings

* CB-4619 chore: map sql editor settings group

* CB-4808 add use short/long object name settings to CloudBeaver

* CB-4808 update default product.conf

* CB-4808 mode short/long settings to SQLModelPreferences

* CB-4808 review fix

* CB-4619 refactor: unit tests

* CB-4619 fix: prevent websocket opening spam

* CB-4619 fix: dependencies

* CB-4619 fix: remove extra theme loading

* CB-4619 fix: rename settings alias resolver file

* CB-4619 fix: after review

* CB-4619 fix conflicts

* CB-4619 rename server property

* CB-4619 remove shortname property

* CB-4619 fix legacy product conf reading

* CB-4619 merge root and runtime configurations

* CB-4619 chore: remove log viewer setting

* CB-4619 integer fields with env variables fix

* CB-4619 fix: property naming

* CB-4619 fix: parseJSONFlat null values

* CB-4619 fix: settings loading

* CB-4619 fix: save only actual settings

---------

Co-authored-by: Aleksandr Skoblikov <aleksandr.skoblikov@dbeaver.com>
Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
Co-authored-by: Evgenia Bezborodova <139753579+EvgeniaBzzz@users.noreply.github.com>
This commit is contained in:
Alexey
2024-03-28 10:30:52 +00:00
committed by GitHub
co-authored by Aleksandr Skoblikov kseniaguzeeva Evgenia Bezborodova
parent b54dd5380d
commit eaa26dfe52
380 changed files with 5094 additions and 3051 deletions
@@ -5,6 +5,6 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { ROOT_SETTINGS_GROUP } from '@cloudbeaver/core-plugin';
import { ROOT_SETTINGS_GROUP } from '@cloudbeaver/core-settings';
export const SQL_EDITOR_SETTINGS_GROUP = ROOT_SETTINGS_GROUP.createSubGroup('plugin_sql_editor_sql_editor_settings_group');
@@ -8,7 +8,7 @@
import { action, makeObservable, observable } from 'mobx';
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { SettingsService } from '@cloudbeaver/core-settings';
import { StorageService } from '@cloudbeaver/core-storage';
import { createSqlDataSourceHistoryInitialState } from '../SqlDataSourceHistory/createSqlDataSourceHistoryInitialState';
import { validateSqlDataSourceHistoryState } from '../SqlDataSourceHistory/validateSqlDataSourceHistoryState';
@@ -22,7 +22,10 @@ const localStorageKey = 'local-storage-sql-data-source';
export class LocalStorageSqlDataSourceBootstrap extends Bootstrap {
private readonly dataSourceStateState = new Map<string, ILocalStorageSqlDataSourceState>();
constructor(private readonly sqlDataSourceService: SqlDataSourceService, settingsService: SettingsService) {
constructor(
private readonly sqlDataSourceService: SqlDataSourceService,
storageService: StorageService,
) {
super();
this.dataSourceStateState = new Map();
@@ -31,7 +34,7 @@ export class LocalStorageSqlDataSourceBootstrap extends Bootstrap {
dataSourceStateState: observable.deep,
});
settingsService.registerSettings(
storageService.registerSettings(
localStorageKey,
this.dataSourceStateState,
() => new Map(),
@@ -71,7 +71,7 @@ export function useTools(state: ISqlEditorTabState): Readonly<State> {
},
checkFileValidity(file: File) {
const maxSize = this.sqlEditorSettingsService.settings.getValue('maxFileSize');
const maxSize = this.sqlEditorSettingsService.maxFileSize;
const size = Math.round(file.size / 1024); // kilobyte
const aboveMaxSize = size > maxSize;
@@ -36,11 +36,11 @@ export type SQLProposal = SqlCompletionProposal;
@injectable()
export class SqlEditorService {
get autoSave() {
return this.sqlEditorSettingsService.settings.getValue('autoSave') && !this.serverConfigResource.isFeatureEnabled(FEATURE_GIT_ID, true);
return this.sqlEditorSettingsService.autoSave && !this.serverConfigResource.isFeatureEnabled(FEATURE_GIT_ID, true);
}
get canInsertAlias() {
return this.sqlEditorSettingsService.proposalInsertTableSettings.getValue('alias');
get insertTableAlias() {
return this.sqlEditorSettingsService.insertTableAlias;
}
constructor(
@@ -18,18 +18,21 @@ import { coreDialogsManifest } from '@cloudbeaver/core-dialogs';
import { coreEventsManifest } from '@cloudbeaver/core-events';
import { coreLocalizationManifest } from '@cloudbeaver/core-localization';
import { coreNavigationTree } from '@cloudbeaver/core-navigation-tree';
import { corePluginManifest } from '@cloudbeaver/core-plugin';
import { coreProductManifest } from '@cloudbeaver/core-product';
import { coreProjectsManifest } from '@cloudbeaver/core-projects';
import { coreRootManifest, ServerConfigResource } from '@cloudbeaver/core-root';
import { createGQLEndpoint } from '@cloudbeaver/core-root/dist/__custom_mocks__/createGQLEndpoint';
import '@cloudbeaver/core-root/dist/__custom_mocks__/expectWebsocketClosedMessage';
import { mockAppInit } from '@cloudbeaver/core-root/dist/__custom_mocks__/mockAppInit';
import { mockGraphQL } from '@cloudbeaver/core-root/dist/__custom_mocks__/mockGraphQL';
import { mockServerConfig } from '@cloudbeaver/core-root/dist/__custom_mocks__/resolvers/mockServerConfig';
import { coreRoutingManifest } from '@cloudbeaver/core-routing';
import { coreSDKManifest } from '@cloudbeaver/core-sdk';
import { coreSettingsManifest } from '@cloudbeaver/core-settings';
import { coreThemingManifest } from '@cloudbeaver/core-theming';
import {
expectDeprecatedSettingMessage,
expectNoDeprecatedSettingMessage,
} from '@cloudbeaver/core-settings/dist/__custom_mocks__/expectDeprecatedSettingMessage';
import { coreStorageManifest } from '@cloudbeaver/core-storage';
import { coreUIManifest } from '@cloudbeaver/core-ui';
import { coreViewManifest } from '@cloudbeaver/core-view';
import { dataViewerManifest } from '@cloudbeaver/plugin-data-viewer';
@@ -40,20 +43,20 @@ import { objectViewerManifest } from '@cloudbeaver/plugin-object-viewer';
import { createApp } from '@cloudbeaver/tests-runner';
import { sqlEditorPluginManifest } from './manifest';
import { SqlEditorSettings, SqlEditorSettingsService } from './SqlEditorSettingsService';
import { SqlEditorSettingsService } from './SqlEditorSettingsService';
const endpoint = createGQLEndpoint();
const server = mockGraphQL(...mockAppInit(endpoint), ...mockAuthentication(endpoint));
const app = createApp(
sqlEditorPluginManifest,
coreLocalizationManifest,
coreEventsManifest,
corePluginManifest,
coreProductManifest,
coreRootManifest,
coreSDKManifest,
coreBrowserManifest,
coreSettingsManifest,
coreViewManifest,
coreStorageManifest,
coreAuthenticationManifest,
coreProjectsManifest,
coreUIManifest,
@@ -63,7 +66,6 @@ const app = createApp(
coreDialogsManifest,
coreNavigationTree,
coreAppManifest,
coreThemingManifest,
datasourceContextSwitchPluginManifest,
navigationTreePlugin,
navigationTabsPlugin,
@@ -72,30 +74,16 @@ const app = createApp(
coreClientActivityManifest,
);
const server = mockGraphQL(...mockAppInit(endpoint), ...mockAuthentication(endpoint));
beforeAll(() => app.init());
const testValueNew = 1;
const testValueDeprecated = 2;
const deprecatedSettings = {
core: {
app: {
sqlEditor: {
maxFileSize: testValueDeprecated,
} as SqlEditorSettings,
},
},
'core.app.sqlEditor.maxFileSize': testValueDeprecated,
};
const newSettings = {
...deprecatedSettings,
plugin: {
'sql-editor': {
maxFileSize: testValueNew,
} as SqlEditorSettings,
},
'plugin.sql-editor.maxFileSize': testValueNew,
};
test('New settings override deprecated settings', async () => {
@@ -106,7 +94,8 @@ test('New settings override deprecated settings', async () => {
await config.refresh();
expect(settings.settings.getValue('maxFileSize')).toBe(testValueNew);
expect(settings.maxFileSize).toBe(testValueNew);
expectNoDeprecatedSettingMessage();
});
test('Deprecated settings are used if new settings are not defined', async () => {
@@ -117,5 +106,6 @@ test('Deprecated settings are used if new settings are not defined', async () =>
await config.refresh();
expect(settings.settings.getValue('maxFileSize')).toBe(testValueDeprecated);
expect(settings.maxFileSize).toBe(testValueDeprecated);
expectDeprecatedSettingMessage();
});
@@ -6,83 +6,152 @@
* you may not use this file except in compliance with the License.
*/
import { Dependency, injectable } from '@cloudbeaver/core-di';
import { ServerSettingsManagerService } from '@cloudbeaver/core-root';
import {
createSettingsAliasResolver,
ESettingsValueType,
PluginManagerService,
PluginSettings,
ISettingDescription,
ROOT_SETTINGS_LAYER,
SettingsManagerService,
} from '@cloudbeaver/core-plugin';
import { ServerSettingsResolverService, ServerSettingsService } from '@cloudbeaver/core-root';
import { schema } from '@cloudbeaver/core-utils';
SettingsProvider,
SettingsProviderService,
SettingsResolverService,
} from '@cloudbeaver/core-settings';
import { schema, schemaExtra } from '@cloudbeaver/core-utils';
import { SQL_EDITOR_SETTINGS_GROUP } from './SQL_EDITOR_SETTINGS_GROUP';
const defaultSettings = schema.object({
maxFileSize: schema.coerce.number().default(10 * 1024), // kilobyte
disabled: schema.coerce.boolean().default(false),
autoSave: schema.coerce.boolean().default(true),
});
const TABLE_ALIAS_OPTIONS = ['NONE', 'PLAIN', 'EXTENDED'] as const;
const defaultProposalInsertTableSettings = schema.object({
alias: schema.coerce.boolean().default(true),
const TABLE_ALIAS_SETTING_OPTIONS = [
{
value: 'NONE',
name: 'ui_disable',
},
{
value: 'PLAIN',
name: 'my_table mt',
},
{
value: 'EXTENDED',
name: 'my_table AS mt',
},
];
const defaultSettings = schema.object({
'plugin.sql-editor.maxFileSize': schema.coerce.number().default(10 * 1024), // kilobyte
'plugin.sql-editor.disabled': schemaExtra.stringedBoolean().default(false),
'plugin.sql-editor.autoSave': schemaExtra.stringedBoolean().default(true),
'sql.proposals.insert.table.alias': schema.coerce
.string()
.transform(value => {
switch (value) {
case 'false':
return 'NONE';
case 'true':
return 'PLAIN';
default:
return value;
}
})
.pipe(schema.enum(TABLE_ALIAS_OPTIONS))
.default('PLAIN'),
});
export type SqlEditorSettings = schema.infer<typeof defaultSettings>;
@injectable()
export class SqlEditorSettingsService extends Dependency {
readonly settings: PluginSettings<typeof defaultSettings>;
readonly proposalInsertTableSettings: PluginSettings<typeof defaultProposalInsertTableSettings>;
get maxFileSize(): number {
return this.settings.getValue('plugin.sql-editor.maxFileSize');
}
get disabled(): boolean {
return this.settings.getValue('plugin.sql-editor.disabled');
}
get autoSave(): boolean {
return this.settings.getValue('plugin.sql-editor.autoSave');
}
get insertTableAlias(): schema.infer<typeof defaultSettings>['sql.proposals.insert.table.alias'] {
return this.settings.getValue('sql.proposals.insert.table.alias');
}
readonly settings: SettingsProvider<typeof defaultSettings>;
constructor(
private readonly pluginManagerService: PluginManagerService,
private readonly settingsProviderService: SettingsProviderService,
private readonly settingsManagerService: SettingsManagerService,
private readonly serverSettingsService: ServerSettingsService,
private readonly serverSettingsResolverService: ServerSettingsResolverService,
private readonly settingsResolverService: SettingsResolverService,
private readonly serverSettingsManagerService: ServerSettingsManagerService,
) {
super();
this.settings = this.pluginManagerService.createSettings('sql-editor', 'plugin', defaultSettings);
this.proposalInsertTableSettings = this.pluginManagerService.createSettings('proposals.insert.table', 'sql', defaultProposalInsertTableSettings);
this.serverSettingsResolverService.addResolver(
this.settings = this.settingsProviderService.createSettings(defaultSettings);
this.settingsResolverService.addResolver(
ROOT_SETTINGS_LAYER,
/** @deprecated Use settings instead, will be removed in 23.0.0 */
createSettingsAliasResolver(this.serverSettingsService, this.settings, 'core.app.sqlEditor'),
createSettingsAliasResolver(this.settingsResolverService, this.settings, {
'plugin.sql-editor.autoSave': 'core.app.sqlEditor.autoSave',
'plugin.sql-editor.maxFileSize': 'core.app.sqlEditor.maxFileSize',
'plugin.sql-editor.disabled': 'core.app.sqlEditor.disabled',
}),
);
this.registerSettings();
}
private registerSettings() {
this.settingsManagerService.registerSettings(this.settings, () => [
// {
// group: SQL_EDITOR_SETTINGS_GROUP,
// key: 'disabled',
// type: ESettingsValueType.Checkbox,
// name: 'Disable SQL editor',
// },
// {
// group: SQL_EDITOR_SETTINGS_GROUP,
// key: 'maxFileSize',
// type: ESettingsValueType.Input,
// name: 'Max file size (KB)',
// description: 'Max file size for SQL editor in kilobytes',
// },
// {
// group: SQL_EDITOR_SETTINGS_GROUP,
// key: 'autoSave',
// type: ESettingsValueType.Checkbox,
// name: 'Auto save',
// description: 'Auto save SQL editor content',
// },
]);
this.serverSettingsManagerService.setGroupOverride('editors/sqlEditor', SQL_EDITOR_SETTINGS_GROUP);
this.serverSettingsManagerService.setSettingTransformer(
'sql.proposals.insert.table.alias',
setting =>
({
...setting,
group: SQL_EDITOR_SETTINGS_GROUP,
name: 'sql_editor_settings_insert_table_aliases_name',
description: 'sql_editor_settings_insert_table_aliases_desc',
options: [...(setting.options?.filter(option => !TABLE_ALIAS_OPTIONS.includes(option.value as any)) || []), ...TABLE_ALIAS_SETTING_OPTIONS],
}) as ISettingDescription<SqlEditorSettings>,
);
this.settingsManagerService.registerSettings(this.proposalInsertTableSettings, () => [
{
group: SQL_EDITOR_SETTINGS_GROUP,
key: 'alias',
type: ESettingsValueType.Checkbox,
name: 'sql_editor_settings_insert_table_aliases_name',
description: 'sql_editor_settings_insert_table_aliases_desc',
},
]);
this.settingsManagerService.registerSettings(this.settings, () => {
const settings: ISettingDescription<SqlEditorSettings>[] = [
// {
// group: SQL_EDITOR_SETTINGS_GROUP,
// key: 'disabled',
// type: ESettingsValueType.Checkbox,
// name: 'Disable SQL editor',
// },
// {
// group: SQL_EDITOR_SETTINGS_GROUP,
// key: 'maxFileSize',
// type: ESettingsValueType.Input,
// name: 'Max file size (KB)',
// description: 'Max file size for SQL editor in kilobytes',
// },
// {
// group: SQL_EDITOR_SETTINGS_GROUP,
// key: 'autoSave',
// type: ESettingsValueType.Checkbox,
// name: 'Auto save',
// description: 'Auto save SQL editor content',
// },
];
if (!this.serverSettingsManagerService.providedSettings.has('sql.proposals.insert.table.alias')) {
settings.push({
key: 'sql.proposals.insert.table.alias',
access: {
scope: ['server', 'client'],
},
group: SQL_EDITOR_SETTINGS_GROUP,
type: ESettingsValueType.Select,
name: 'sql_editor_settings_insert_table_aliases_name',
description: 'sql_editor_settings_insert_table_aliases_desc',
options: TABLE_ALIAS_SETTING_OPTIONS,
});
}
return settings;
});
}
}