feat: settings service

This commit is contained in:
Wroud
2020-04-27 19:14:12 +03:00
parent 96de471094
commit a537835504
40 changed files with 369 additions and 55 deletions
@@ -12,11 +12,14 @@ const modules = [
// The order of modules is important.
// The module must be built before it is used in another module
'/extensions',
'/di',
'/utils',
'/eventsLog',
'/settings',
'/product',
'/plugin',
'/theming',
'/localization',
@@ -14,6 +14,9 @@ module.exports = (env, argv) => merge(commonConfig(env, argv), {
dialogs: "./dialogs/index.ts",
eventsLog: "./eventsLog/index.ts",
localization: "./localization/index.ts",
extensions: "./extensions/index.ts",
plugin: "./plugin/index.ts",
product: "./product/index.ts",
// queries dir - ignore
root: "./root/index.ts",
sdk: "./sdk/index.ts",
+8
View File
@@ -0,0 +1,8 @@
{
"name": "@dbeaver/core/plugin",
"private": true,
"sideEffects": false,
"main": "../dist/plugin",
"module": "../dist/plugin",
"types": "../dist/types/plugin"
}
@@ -0,0 +1,8 @@
{
"name": "@dbeaver/core/product",
"private": true,
"sideEffects": false,
"main": "../dist/product",
"module": "../dist/product",
"types": "../dist/types/product"
}
@@ -0,0 +1,25 @@
/*
* cloudbeaver - Cloud Database Manager
* Copyright (C) 2020 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { injectable } from '@dbeaver/core/di';
import { PluginManagerService } from '@dbeaver/core/plugin';
const defaultSettings = {
'app.logViewer.refreshInterval': 3000,
'app.logViewer.maxLogEntries': 1000,
};
export type CoreSettings = typeof defaultSettings
@injectable()
export class CoreSettingsService {
readonly settings = this.pluginManagerService.getPluginSettings('core', defaultSettings);
constructor(private pluginManagerService: PluginManagerService) { }
}
+1 -1
View File
@@ -12,9 +12,9 @@ import styled, { css } from 'reshadow';
import {
Pane, ResizerControls, Split, splitHorizontalStyles, splitStyles,
} from '@dbeaver/core/blocks';
import { useController } from '@dbeaver/core/di';
import { composes, useStyles } from '@dbeaver/core/theming';
import { useController } from '../di';
import { NavigationTabsBar } from './shared/NavigationTabs/NavigationTabsBar';
import { LogViewTab } from './shared/ToolsPanel/LogViewTab/LogViewTab';
import { LogViewTabController } from './shared/ToolsPanel/LogViewTab/LogViewTabController';
@@ -6,7 +6,7 @@
* you may not use this file except in compliance with the License.
*/
import { createExtension, isExtension, IExtension } from '../../../../extensions';
import { createExtension, isExtension, IExtension } from '@dbeaver/core/extensions';
const connectionProviderSymbol = Symbol('@extension/ConnectionProvider');
@@ -6,7 +6,7 @@
* you may not use this file except in compliance with the License.
*/
import { createExtension, isExtension, IExtension } from '../../../../extensions';
import { createExtension, isExtension, IExtension } from '@dbeaver/core/extensions';
const connectionSetterSymbol = Symbol('@extension/ConnectionSetter');
@@ -6,8 +6,9 @@
* you may not use this file except in compliance with the License.
*/
import { IExtension } from '../../../extensions';
import { Style } from '../../../theming';
import { IExtension } from '@dbeaver/core/extensions';
import { Style } from '@dbeaver/core/theming';
import { ITab } from './ITab';
export type TabHandlerTabProps<T = any> = {
@@ -6,7 +6,7 @@
* you may not use this file except in compliance with the License.
*/
import { createExtension, isExtension, IExtension } from '../../../../extensions';
import { createExtension, isExtension, IExtension } from '@dbeaver/core/extensions';
const objectCatalogProviderSymbol = Symbol('@extension/ObjectCatalogProvider');
@@ -6,7 +6,7 @@
* you may not use this file except in compliance with the License.
*/
import { createExtension, isExtension, IExtension } from '../../../../extensions';
import { createExtension, isExtension, IExtension } from '@dbeaver/core/extensions';
const objectCatalogSetterSymbol = Symbol('@extension/ObjectCatalogSetter');
@@ -6,7 +6,7 @@
* you may not use this file except in compliance with the License.
*/
import { createExtension, isExtension, IExtension } from '../../../../extensions';
import { createExtension, isExtension, IExtension } from '@dbeaver/core/extensions';
const objectSchemaProviderSymbol = Symbol('@extension/ObjectSchemaProvider');
@@ -6,7 +6,7 @@
* you may not use this file except in compliance with the License.
*/
import { createExtension, isExtension, IExtension } from '../../../../extensions';
import { createExtension, isExtension, IExtension } from '@dbeaver/core/extensions';
const objectSchemaSetterSymbol = Symbol('@extension/ObjectSchemaSetter');
@@ -11,16 +11,11 @@ import { action, observable } from 'mobx';
import { injectable } from '@dbeaver/core/di';
import { NotificationService } from '@dbeaver/core/eventsLog';
import { GraphQLService } from '@dbeaver/core/sdk';
import { SettingsService } from '@dbeaver/core/settings';
import { uuid } from '@dbeaver/core/utils';
import { CoreSettingsService } from '../../../../CoreSettingsService';
import { ILogEntry } from './ILogEntry';
const REFRESH_INTERVAL = 3000;
const REFRESH_INTERVAL_TOKEN = 'core.app.logViewer.refreshInterval';
const MAX_LOG_ENTRIES = 1000;
const MAX_LOG_ENTRIES_TOKEN = 'core.app.logViewer.maxLogEntries';
@injectable()
export class LogViewerService {
@@ -28,11 +23,9 @@ export class LogViewerService {
@observable private log: ILogEntry[] = [];
private interval: any = null;
private refreshInterval = this.settingsService.getProperty(REFRESH_INTERVAL_TOKEN, REFRESH_INTERVAL);
private maxLogEntries = this.settingsService.getProperty(MAX_LOG_ENTRIES_TOKEN, MAX_LOG_ENTRIES);
constructor(private graphQLService: GraphQLService,
private settingsService: SettingsService,
private coreSettingsService: CoreSettingsService,
private notificationService: NotificationService) {
}
@@ -55,9 +48,10 @@ export class LogViewerService {
return;
}
await this.updateLog();
const refreshInterval = this.coreSettingsService.settings.getValue('app.logViewer.refreshInterval');
this.interval = setInterval(() => {
this.updateLog();
}, this.refreshInterval);
}, refreshInterval);
}
stopLog() {
@@ -84,14 +78,16 @@ export class LogViewerService {
@action
addNewEntries(entries: ILogEntry[]) {
this.log.unshift(...entries.reverse());
if (this.log.length > this.maxLogEntries) {
this.log.splice(this.maxLogEntries, this.log.length - this.maxLogEntries);
const maxLogEntries = this.coreSettingsService.settings.getValue('app.logViewer.maxLogEntries');
if (this.log.length > maxLogEntries) {
this.log.splice(maxLogEntries, this.log.length - maxLogEntries);
}
}
async loadLog(): Promise<ILogEntry[]> {
const maxLogEntries = this.coreSettingsService.settings.getValue('app.logViewer.maxLogEntries');
const { log } = await this.graphQLService.gql.readSessionLog({
maxEntries: this.maxLogEntries,
maxEntries: maxLogEntries,
clearEntries: true,
});
const entries: ILogEntry[] = (log || []).map(item => ({
@@ -9,8 +9,9 @@
import { observer } from 'mobx-react';
import styled, { css } from 'reshadow';
import { useTranslate } from '../localization';
import { useStyles, composes } from '../theming';
import { useTranslate } from '@dbeaver/core/localization';
import { useStyles, composes } from '@dbeaver/core/theming';
import { Button } from './Button';
@@ -8,7 +8,7 @@
import styled, { css, use } from 'reshadow';
import { useStyles } from '../theming';
import { useStyles } from '@dbeaver/core/theming';
const styles = css`
label {
@@ -8,7 +8,7 @@
import { css } from 'reshadow';
import { composes } from '../../theming/themeUtils';
import { composes } from '@dbeaver/core/theming';
export const loaderStyles = css`
loader {
@@ -9,8 +9,7 @@
import { ButtonHTMLAttributes, PropsWithChildren } from 'react';
import { TLocalizationToken } from '@dbeaver/core/localization';
import { Style } from '../../theming/useStyles';
import { Style } from '@dbeaver/core/theming';
export type MenuMod = 'primary' | 'surface' | 'secondary';
@@ -10,10 +10,9 @@ import { Subject } from 'rxjs';
import { injectable } from '@dbeaver/core/di';
import {
GQLError, GraphQLService,
GQLError, GraphQLService, EServerErrorCode
} from '@dbeaver/core/sdk';
import { EServerErrorCode } from '../../sdk/EServerErrorCode';
import { CommonDialogService } from '../CommonDialog/CommonDialogService';
import { SessionExpiredDialog } from './SessionExpiredDialog';
+13 -1
View File
@@ -17,11 +17,17 @@ import { PluginManifest } from '@dbeaver/core/di';
import { CommonDialogService, ContextMenuService, SessionExpireService } from '@dbeaver/core/dialogs';
import { NotificationService, ExceptionsCatcherService } from '@dbeaver/core/eventsLog';
import { LocalizationService } from '@dbeaver/core/localization';
import { SessionService, ServerService } from '@dbeaver/core/root';
import { PluginManagerService } from '@dbeaver/core/plugin';
import { ProductManagerService, ProductSettingsService } from '@dbeaver/core/product';
import {
SessionService, ServerService, SessionSettingsService, ServerSettingsService
} from '@dbeaver/core/root';
import { EnvironmentService, GraphQLService } from '@dbeaver/core/sdk';
import { LocalStorageSaveService, SettingsService } from '@dbeaver/core/settings';
import { ThemeService } from '@dbeaver/core/theming';
import { CoreSettingsService } from './CoreSettingsService';
export const coreManifest: PluginManifest = {
info: {
name: 'DBeaver core',
@@ -29,6 +35,12 @@ export const coreManifest: PluginManifest = {
depends: [],
providers: [
ProductSettingsService,
ProductManagerService,
PluginManagerService,
ServerSettingsService,
SessionSettingsService,
CoreSettingsService,
CommonDialogService,
SessionExpireService,
ConnectionDialogsService,
@@ -0,0 +1,23 @@
/*
* cloudbeaver - Cloud Database Manager
* Copyright (C) 2020 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { injectable } from '@dbeaver/core/di';
import { ProductManagerService } from '@dbeaver/core/product';
import { PluginSettings } from './PluginSettings';
@injectable()
export class PluginManagerService {
constructor(private productManagerService: ProductManagerService) { }
getPluginSettings<T>(scope: string, defaults: T) {
return new PluginSettings(this.productManagerService.settings, scope, defaults);
}
}
@@ -0,0 +1,38 @@
/*
* cloudbeaver - Cloud Database Manager
* Copyright (C) 2020 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { ISettingsSource } from '@dbeaver/core/settings';
export class PluginSettings<T> implements ISettingsSource {
constructor(
private source: ISettingsSource,
private scope: string,
private defaults: T
) {
}
has<TKey extends keyof T>(key: TKey): boolean {
return true;
}
getValue<TKey extends keyof T>(key: TKey): T[TKey] {
if (this.source.has(this.scopedKey(key))) {
return this.source.getValue(this.scopedKey(key));
}
return this.defaults[key];
}
setValue<TKey extends keyof T>(key: TKey, value: T[TKey]) {
this.source.setValue(this.scopedKey(key), value);
}
private scopedKey(key: string | number | symbol): string {
return `${this.scope}.${String(key)}`;
}
}
+2
View File
@@ -0,0 +1,2 @@
export * from './PluginSettings';
export * from './PluginManagerService';
@@ -0,0 +1,30 @@
/*
* cloudbeaver - Cloud Database Manager
* Copyright (C) 2020 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { injectable } from '@dbeaver/core/di';
import { ServerService } from '@dbeaver/core/root';
import { parseJSONFlat } from '@dbeaver/core/utils';
import { ProductSettingsService } from './ProductSettingsService';
@injectable()
export class ProductManagerService {
readonly settings = new ProductSettingsService(this.serverService.settings)
constructor(private serverService: ServerService) { }
setSettings(object: any) {
parseJSONFlat(
object,
this.settings.setSelfValue.bind(this.settings),
undefined
);
}
}
@@ -0,0 +1,26 @@
/*
* cloudbeaver - Cloud Database Manager
* Copyright (C) 2020 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { injectable } from '@dbeaver/core/di';
import { SettingsSource, ISettingsSource } from '@dbeaver/core/settings';
@injectable()
export class ProductSettingsService extends SettingsSource {
constructor(fallback: ISettingsSource) {
super(fallback);
}
setValue(key: string, value: any) {
this.fallback?.setValue(key, value);
}
setSelfValue(key: string, value: string) {
super.setValue(key, value);
}
}
@@ -0,0 +1,2 @@
export * from './ProductManagerService';
export * from './ProductSettingsService';
+20 -2
View File
@@ -8,12 +8,21 @@
import { injectable } from '@dbeaver/core/di';
import { GraphQLService, ServerConfig, CachedResource } from '@dbeaver/core/sdk';
import { parseJSONFlat } from '@dbeaver/core/utils';
import { ServerSettingsService } from './ServerSettingsService';
import { SessionService } from './SessionService';
@injectable()
export class ServerService {
config = new CachedResource(undefined, this.refreshConfigAsync.bind(this));
readonly config = new CachedResource(undefined, this.refreshConfigAsync.bind(this));
readonly settings = new ServerSettingsService(this.sessionService.settings);
constructor(private graphQLService: GraphQLService) {
private lastConfig: any = null
constructor(
private graphQLService: GraphQLService,
private sessionService: SessionService,
) {
}
private async refreshConfigAsync(data: ServerConfig | undefined): Promise<ServerConfig> {
@@ -23,6 +32,15 @@ export class ServerService {
const { serverConfig } = await this.graphQLService.gql.serverConfig();
if (serverConfig.productConfiguration !== this.lastConfig) {
this.lastConfig = serverConfig.productConfiguration;
this.settings.clear();
parseJSONFlat(
serverConfig.productConfiguration,
this.settings.setSelfValue.bind(this.settings),
undefined
);
}
return serverConfig;
}
}
@@ -0,0 +1,30 @@
/*
* cloudbeaver - Cloud Database Manager
* Copyright (C) 2020 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { injectable } from '@dbeaver/core/di';
import { SettingsSource, ISettingsSource } from '@dbeaver/core/settings';
@injectable()
export class ServerSettingsService extends SettingsSource {
constructor(fallback: ISettingsSource) {
super(fallback);
}
setValue(key: string, value: string) {
this.fallback?.setValue(key, value);
}
setSelfValue(key: string, value: string) {
super.setValue(key, value);
}
clear() {
this.store.clear();
}
}
@@ -11,6 +11,7 @@ import {
ConnectionInfo, GraphQLService, SessionInfo, CachedResource,
} from '@dbeaver/core/sdk';
import { SessionSettingsService } from './SessionSettingsService';
export type SessionState = Pick<SessionInfo, 'id' | 'createTime' | 'lastAccessTime' | 'locale'> & {
connections: Array<Pick<ConnectionInfo, 'id' | 'name' | 'connected' | 'driverId'>>;
@@ -18,7 +19,8 @@ export type SessionState = Pick<SessionInfo, 'id' | 'createTime' | 'lastAccessTi
@injectable()
export class SessionService {
session = new CachedResource(undefined, this.refreshSessionStateAsync.bind(this));
readonly session = new CachedResource(undefined, this.refreshSessionStateAsync.bind(this));
readonly settings = new SessionSettingsService('session_settings');
constructor(private graphQLService: GraphQLService) {}
@@ -0,0 +1,27 @@
/*
* cloudbeaver - Cloud Database Manager
* Copyright (C) 2020 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { injectable } from '@dbeaver/core/di';
import { SettingsSource } from '@dbeaver/core/settings';
@injectable()
export class SessionSettingsService extends SettingsSource {
constructor(private localStorageKey: string) {
super();
const state = localStorage.getItem(this.localStorageKey);
if (state) {
this.store = JSON.parse(state);
}
}
setValue(key: string, value: string) {
super.setValue(key, value);
localStorage.setItem(this.localStorageKey, JSON.stringify(this.store));
}
}
+2
View File
@@ -1,2 +1,4 @@
export * from './SessionService';
export * from './ServerService';
export * from './ServerSettingsService';
export * from './SessionSettingsService';
+1
View File
@@ -4,3 +4,4 @@ export * from './sdk';
export * from './GQLError';
export * from './ServerInternalError';
export * from './CachedResource';
export * from './EServerErrorCode';
@@ -0,0 +1,13 @@
/*
* cloudbeaver - Cloud Database Manager
* Copyright (C) 2020 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
export interface ISettingsSource {
has(key: any): boolean;
getValue(key: any): any | undefined;
setValue(key: any, value: any): void;
}
@@ -10,28 +10,10 @@ import { injectable } from '@dbeaver/core/di';
import { LocalStorageSaveService } from './LocalStorageSaveService';
const PLUGIN_SETTINGS_KEY = 'pluginSettings';
@injectable()
export class SettingsService {
private properties = new Map<string, any>();
constructor(private localStorageSaveService: LocalStorageSaveService) {
}
/**
*
* @param name - expected in format {plugin-id}.{scope-id}.{property-name}, default scope-id = main.
* @param defaultValue
*/
getProperty<T>(name: string, defaultValue: T): T {
if (this.properties.has(name)) {
return this.properties.get(name) as T;
}
this.properties.set(name, defaultValue);
return defaultValue;
}
constructor(private localStorageSaveService: LocalStorageSaveService) { }
/**
* if autosaved value exits the settings object will be populated with this value
@@ -39,6 +21,6 @@ export class SettingsService {
* @param settings - observable object expected
*/
registerSettings<T extends object>(settings: T, key: string): void {
this.localStorageSaveService.withAutoSave(settings, PLUGIN_SETTINGS_KEY);
this.localStorageSaveService.withAutoSave(settings, key);
}
}
@@ -0,0 +1,31 @@
/*
* cloudbeaver - Cloud Database Manager
* Copyright (C) 2020 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { ISettingsSource } from './ISettingsSource';
export class SettingsSource implements ISettingsSource {
protected store = new Map<string, any>()
constructor(protected fallback?: ISettingsSource) {}
has(key: string): boolean {
if (this.store.has(key)) {
return true;
}
return !!this.fallback?.has(key);
}
getValue(key: string): any | undefined {
if (this.fallback?.has(key)) {
return this.fallback.getValue(key);
}
return this.store.get(key);
}
setValue(key: string, value: any) {
this.store.set(key, value);
}
}
@@ -1,2 +1,4 @@
export * from './SettingsService';
export * from './LocalStorageSaveService';
export * from './SettingsSource';
export * from './ISettingsSource';
+1
View File
@@ -1,5 +1,6 @@
export * from './OrderedMap';
export * from './uuid';
export * from './parseJSONFlat';
export * from './Promises/CancellablePromise';
export * from './Promises/Deferred';
@@ -0,0 +1,23 @@
/*
* cloudbeaver - Cloud Database Manager
* Copyright (C) 2020 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
export function parseJSONFlat(
object: any,
setValue: (key: string, value: any) => void,
scope?: string
) {
if (typeof object === 'object') {
for (const [key, value] of Object.entries<any>(object)) {
parseJSONFlat(value, setValue, `${scope ? `${scope}.` : ''}${key}`);
}
return;
}
if (scope) {
setValue(scope, object);
}
}
@@ -46,6 +46,9 @@ module.exports = (env, argv) => merge(commonConfig(env, argv), {
"@dbeaver/core/settings": path.resolve(__dirname, "../../../core/src/settings"),
"@dbeaver/core/theming": path.resolve(__dirname, "../../../core/src/theming"),
"@dbeaver/core/utils": path.resolve(__dirname, "../../../core/src/utils"),
"@dbeaver/core/extensions": path.resolve(__dirname, "../../../core/src/extensions"),
"@dbeaver/core/product": path.resolve(__dirname, "../../../core/src/product"),
"@dbeaver/core/plugin": path.resolve(__dirname, "../../../core/src/plugin"),
// rewrite imports for unnecessary work in `web/src/libs/sdk.ts`
'graphql-tag': path.join(__dirname, '../../fix-gql.js'),
+4 -1
View File
@@ -16,7 +16,10 @@
"@dbeaver/core/sdk": ["packages/core/src/sdk"],
"@dbeaver/core/settings": ["packages/core/src/settings"],
"@dbeaver/core/theming": ["packages/core/src/theming"],
"@dbeaver/core/utils": ["packages/core/src/utils"]
"@dbeaver/core/utils": ["packages/core/src/utils"],
"@dbeaver/core/extensions": ["packages/core/src/extensions"],
"@dbeaver/core/plugin": ["packages/core/src/plugin"],
"@dbeaver/core/product": ["packages/core/src/product"]
}
}
}