refactor(core): split Session servicerefactor(core): specify gql schema types

This commit is contained in:
Wroud
2020-04-27 14:38:43 +03:00
parent 7bf7ca070e
commit 96de471094
17 changed files with 250 additions and 138 deletions
@@ -55,14 +55,14 @@ type WebServiceConfig {
}
type ServerConfig {
name: String
version: String
name: String!
version: String!
supportsPredefinedConnections: Boolean
supportsProvidedConnections: Boolean
supportsCustomConnections: Boolean
supportsConnectionBrowser: Boolean
supportsWorkspaces: Boolean
supportedLanguages: [ ServerLanguage ]
supportedLanguages: [ ServerLanguage ]!
services: [ WebServiceConfig ]
productConfiguration: Object!
}
@@ -70,12 +70,12 @@ type ServerConfig {
type SessionInfo {
id: ID!
createTime: String
lastAccessTime: String
locale: String
createTime: String!
lastAccessTime: String!
locale: String!
serverMessages: [ ServerMessage ]
connections: [ ConnectionInfo ]
connections: [ ConnectionInfo ]!
}
####################################################
+7 -3
View File
@@ -14,9 +14,11 @@ import {
} from '@dbeaver/core/app';
import { injectable } from '@dbeaver/core/di';
import { ExceptionsCatcherService } from '@dbeaver/core/eventsLog';
import { SessionService } from '@dbeaver/core/root';
import { ThemeService } from '@dbeaver/core/theming';
import { SessionExpireService } from './dialogs';
import { LocalizationService } from './localization';
/**
* AppBootstrap.init() will be executed between first and second phase of App initialization,
* when application has registered all dependencies but plugins did'nt initialized.
@@ -27,11 +29,12 @@ import { ThemeService } from '@dbeaver/core/theming';
export class AppBootstrap {
constructor(private exceptionsCatcherService: ExceptionsCatcherService,
private sessionService: SessionService,
private localizationService: LocalizationService,
private themeService: ThemeService,
private connectionDialogService: ConnectionDialogsService,
private connectionsManager: ConnectionsManagerService,
private logViewerMenuService: LogViewerMenuService,
private sessionExpireService: SessionExpireService,
private navigationTreeContextMenuService: NavigationTreeContextMenuService,
private nodesManagerService: NodesManagerService,
private navigationTabsService: NavigationTabsService,
@@ -40,8 +43,9 @@ export class AppBootstrap {
async init() {
this.exceptionsCatcherService.subscribe();
this.sessionExpireService.subscribe();
await this.sessionService.init();
await this.localizationService.init();
await this.themeService.init();
this.connectionSchemaManagerService.registerCallbacks();
@@ -11,7 +11,7 @@ import styled from 'reshadow';
import { Icon } from '@dbeaver/core/blocks';
import { useService } from '@dbeaver/core/di';
import { SessionService } from '@dbeaver/core/root';
import { ServerService } from '@dbeaver/core/root';
import { useStyles } from '@dbeaver/core/theming';
import { ConnectionSelector } from './ConnectionSchemaManager/ConnectionSelector/ConnectionSelector';
@@ -23,8 +23,8 @@ import { topNavBarStyles } from './topNavBarStyles';
declare const version: string; // declared in webpack DefinePlugin // todo move to enviroment?
export const TopNavBar = observer(function TopNavBar() {
const sessionService = useService(SessionService);
const title = `Frontend: ${version}\nBackend: ${sessionService.version}`;
const serverService = useService(ServerService);
const title = `Frontend: ${version}\nBackend: ${serverService.config.data?.version}`;
return styled(useStyles(topNavBarStyles))(
<header>
@@ -10,13 +10,12 @@ import { computed, observable } from 'mobx';
import { Subject } from 'rxjs';
import { injectable } from '@dbeaver/core/di';
import { ConnectionShortInfo, SessionService } from '@dbeaver/core/root';
import { SessionService } from '@dbeaver/core/root';
import {
ConnectionInfo,
DataSourceInfo,
DriverInfo,
GraphQLService,
NavGetStructContainersQuery,
CachedResource,
DatabaseObjectInfo,
} from '@dbeaver/core/sdk';
@@ -118,9 +117,16 @@ export class ConnectionsManagerService {
}
async restoreConnections() {
for (const connection of this.sessionService.getConnections()) {
this.restoreConnection(connection);
const config = await this.sessionService.session.load();
if (!config) {
return;
}
// TODO: connections must be string[]
for (const connection of config.connections) {
await this.restoreConnection(connection);
}
await this.nodesManagerService.updateRootChildren();
}
private async refreshObjectContainersAsync(
@@ -130,7 +136,10 @@ export class ConnectionsManagerService {
catalogId?: string,
): Promise<Map<string, ObjectContainer[]>> {
if (refresh || !data.has(connectionId)) {
const { navGetStructContainers } = await this.loadSchemasAndCatalogs(connectionId, catalogId);
const { navGetStructContainers } = await this.graphQLService.gql.navGetStructContainers({
connectionId,
catalogId,
});
data.set(connectionId, [...navGetStructContainers.schemaList, ...navGetStructContainers.catalogList]);
}
@@ -149,18 +158,10 @@ export class ConnectionsManagerService {
return data;
}
/**
* Note that this request returns either schemaList or catalogList. You never got both lists together
*/
private async loadSchemasAndCatalogs(connectionId: string, catalogId?: string): Promise<NavGetStructContainersQuery> {
return this.graphQLService.gql.navGetStructContainers({ connectionId, catalogId });
}
private async restoreConnection(connection: Connection) {
// TODO: Must be loaded based on connection id
// const { connection } = await this.graphQLService.gql.connectionState({ id });
private restoreConnection(connectionInfo: ConnectionShortInfo) {
const connection: Connection = {
...connectionInfo,
connected: true,
};
this.connectionsMap.set(connection.id, connection);
this.onOpenConnection.next(connection);
}
@@ -0,0 +1,8 @@
query connectionState($id: ID!) {
connection: connectionState(id: $id) {
id
name
driverId
connected
}
}
@@ -0,0 +1,49 @@
/*
* 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 { Subject } from 'rxjs';
import { injectable } from '@dbeaver/core/di';
import {
GQLError, GraphQLService,
} from '@dbeaver/core/sdk';
import { EServerErrorCode } from '../../sdk/EServerErrorCode';
import { CommonDialogService } from '../CommonDialog/CommonDialogService';
import { SessionExpiredDialog } from './SessionExpiredDialog';
@injectable()
export class SessionExpireService {
private isNotifiedAboutExpiredSession = false;
onSessionExpire = new Subject();
constructor(
private graphQLService: GraphQLService,
private commonDialogService: CommonDialogService
) {
}
subscribe() {
this.graphQLService.registerInterceptor(this.sessionExpiredInterceptor.bind(this));
}
private async sessionExpiredInterceptor(request: Promise<any>): Promise<any> {
try {
return await request;
} catch (exception) {
if (exception instanceof GQLError
&& exception.errorCode === EServerErrorCode.sessionExpired
&& !this.isNotifiedAboutExpiredSession) {
this.isNotifiedAboutExpiredSession = true;
this.onSessionExpire.next();
await this.commonDialogService.open(SessionExpiredDialog, null);
}
throw exception;
}
}
}
@@ -11,10 +11,12 @@ import { useCallback } from 'react';
import styled, { css } from 'reshadow';
import { Button } from '@dbeaver/core/blocks';
import { CommonDialogWrapper, DialogComponent } from '@dbeaver/core/dialogs';
import { useTranslate } from '@dbeaver/core/localization';
import { useStyles } from '@dbeaver/core/theming';
import { DialogComponent } from '../CommonDialog/CommonDialogService';
import { CommonDialogWrapper } from '../CommonDialog/CommonDialogWrapper';
const styles = css`
dialog-content {
display: flex;
@@ -13,3 +13,5 @@ export * from './Menu/models/ComputedMenuPanelModel';
export * from './Menu/ContextMenu/ContextMenuService';
export * from './Menu/ContextMenu/IContextMenuItem';
export * from './Menu/ContextMenu/IMenuContext';
export * from './SessionExpire/SessionExpireService';
@@ -14,6 +14,7 @@ import { NotificationService } from '@dbeaver/core/eventsLog';
import { GraphQLService, ServerLanguage } from '@dbeaver/core/sdk';
import { SettingsService } from '@dbeaver/core/settings';
import { SessionService, ServerService } from '../root';
import { Locale } from './Locale';
import { TLocalizationToken } from './TLocalizationToken';
@@ -37,6 +38,8 @@ export class LocalizationService {
private localeMap: Map<string, Locale> = new Map();
constructor(private notificationService: NotificationService,
private sessionService: SessionService,
private serverService: ServerService,
private graphQLService: GraphQLService,
private settingsService: SettingsService) {
@@ -78,13 +81,19 @@ export class LocalizationService {
return locale && locale[token];
}
async init(sessionLanguage: string | undefined, supportedLanguages: ServerLanguageShort[]) {
this.supportedLanguages = supportedLanguages;
async init() {
const session = await this.sessionService.session.load();
const config = await this.serverService.config.load();
if (!session || !config) {
return;
}
this.supportedLanguages = config.supportedLanguages;
this.settingsService.registerSettings(this.settings, LANG_SETTINGS_KEY); // overwrite default value with settings
if (sessionLanguage) {
this.setCurrentLocale(sessionLanguage); // session language wins
}
this.setCurrentLocale(session.locale); // session language wins
await this.setLocale(this.getCurrentLanguage());
}
+4 -2
View File
@@ -14,10 +14,10 @@ import {
SettingsMenuService, LogViewerService, LogViewerMenuService,
} from '@dbeaver/core/app';
import { PluginManifest } from '@dbeaver/core/di';
import { CommonDialogService, ContextMenuService } from '@dbeaver/core/dialogs';
import { CommonDialogService, ContextMenuService, SessionExpireService } from '@dbeaver/core/dialogs';
import { NotificationService, ExceptionsCatcherService } from '@dbeaver/core/eventsLog';
import { LocalizationService } from '@dbeaver/core/localization';
import { SessionService } from '@dbeaver/core/root';
import { SessionService, ServerService } from '@dbeaver/core/root';
import { EnvironmentService, GraphQLService } from '@dbeaver/core/sdk';
import { LocalStorageSaveService, SettingsService } from '@dbeaver/core/settings';
import { ThemeService } from '@dbeaver/core/theming';
@@ -30,6 +30,7 @@ export const coreManifest: PluginManifest = {
providers: [
CommonDialogService,
SessionExpireService,
ConnectionDialogsService,
ConnectionSchemaManagerService,
ConnectionsManagerService,
@@ -52,6 +53,7 @@ export const coreManifest: PluginManifest = {
SettingsMenuService,
SettingsService,
ThemeService,
ServerService,
],
async initialize(services) {
@@ -0,0 +1,28 @@
/*
* 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 { GraphQLService, ServerConfig, CachedResource } from '@dbeaver/core/sdk';
@injectable()
export class ServerService {
config = new CachedResource(undefined, this.refreshConfigAsync.bind(this));
constructor(private graphQLService: GraphQLService) {
}
private async refreshConfigAsync(data: ServerConfig | undefined): Promise<ServerConfig> {
if (data) {
return data;
}
const { serverConfig } = await this.graphQLService.gql.serverConfig();
return serverConfig;
}
}
+10 -79
View File
@@ -6,98 +6,29 @@
* you may not use this file except in compliance with the License.
*/
import { computed } from 'mobx';
import { injectable } from '@dbeaver/core/di';
import { CommonDialogService } from '@dbeaver/core/dialogs';
import { LocalizationService } from '@dbeaver/core/localization';
import {
ConnectionInfo, GQLError, GraphQLService, ServerLanguage, SessionInfo,
ConnectionInfo, GraphQLService, SessionInfo, CachedResource,
} from '@dbeaver/core/sdk';
import { EServerErrorCode } from '../sdk/EServerErrorCode';
import { SessionExpiredDialog } from './SessionExpiredDialog';
export type ConnectionShortInfo = Pick<ConnectionInfo, 'id' | 'name' | 'driverId'>;
export type SessionShortInfo = Pick<SessionInfo, 'locale' | 'connections'>;
export type Session = {
name: string;
version: string;
language: string;
supportedLanguages: Pick<ServerLanguage, 'isoCode' | 'nativeName'>[];
connections: ConnectionShortInfo[];
}
export type Server = {
name: string;
version: string;
supportedLanguages: ConnectionShortInfo[];
}
export type SessionState = Pick<SessionInfo, 'id' | 'createTime' | 'lastAccessTime' | 'locale'> & {
connections: Array<Pick<ConnectionInfo, 'id' | 'name' | 'connected' | 'driverId'>>;
};
@injectable()
export class SessionService {
private session: Session = {
name: '',
version: '',
language: 'en',
supportedLanguages: [],
connections: [],
};
session = new CachedResource(undefined, this.refreshSessionStateAsync.bind(this));
@computed get version() {
return this.session.version;
}
constructor(private graphQLService: GraphQLService) {}
private isNotifiedAboutExpiredSession = false;
constructor(private graphQLService: GraphQLService,
private localizationService: LocalizationService,
private commonDialogService: CommonDialogService) {
this.graphQLService.registerInterceptor(this.sessionExpiredInterceptor.bind(this));
}
getConnections(): ConnectionShortInfo[] {
return this.session.connections;
}
async init(): Promise<void> {
this.session = await this.fetchCurrentSession();
this.localizationService.init(this.session.language, this.session.supportedLanguages);
}
private async sessionExpiredInterceptor(request: Promise<any>): Promise<any> {
try {
return await request;
} catch (exception) {
if (exception instanceof GQLError
&& exception.errorCode === EServerErrorCode.sessionExpired
&& !this.isNotifiedAboutExpiredSession) {
this.isNotifiedAboutExpiredSession = true;
await this.commonDialogService.open(SessionExpiredDialog, null);
}
throw exception;
private async refreshSessionStateAsync(data: SessionState | undefined): Promise<SessionState> {
if (data) {
return data;
}
}
private async fetchCurrentSession(): Promise<Session> {
const { serverConfig } = await this.graphQLService.gql.serverConfig();
const sessionInfo = await this.openNewSession();
const { session } = await this.graphQLService.gql.openSession();
const session: Session = {
name: serverConfig.name || 'WHERE IS A NAME',
version: serverConfig.version || 'WHERE IS A VERSION',
language: sessionInfo.locale || 'en',
supportedLanguages: serverConfig.supportedLanguages,
connections: sessionInfo.connections || [],
};
return session;
}
private async openNewSession(): Promise<SessionShortInfo> {
const response = await this.graphQLService.gql.openSession();
return response.session;
}
}
+1
View File
@@ -1 +1,2 @@
export * from './SessionService';
export * from './ServerService';
@@ -1,10 +1,14 @@
mutation openSession {
session: openSession {
id
createTime
lastAccessTime
locale
connections {
id
name,
name
driverId
connected
}
locale
}
}
@@ -2,9 +2,17 @@ query serverConfig {
serverConfig {
name
version
productConfiguration
supportsPredefinedConnections
supportsProvidedConnections
supportsCustomConnections
supportsConnectionBrowser
supportsWorkspaces
supportedLanguages {
isoCode
displayName
nativeName
}
productConfiguration
}
}
@@ -1,10 +1,14 @@
query sessionState {
sessionState {
id
createTime
lastAccessTime
locale
connections {
id
name,
name
driverId
connected
}
locale
}
}
+77 -18
View File
@@ -24,6 +24,11 @@ export type AsyncTaskInfo = {
status?: Maybe<Scalars["String"]>;
error?: Maybe<ServerError>;
result?: Maybe<SqlExecuteInfo>;
/**
* Task result.
* Can be some kind of identifier to obtain real result using another API function
*/
taskResult?: Maybe<Scalars["Object"]>;
};
export type AsyncTaskResult = SqlExecuteInfo;
@@ -411,14 +416,14 @@ export type QueryMetadataGetNodeDdlArgs = {
};
export type ServerConfig = {
name?: Maybe<Scalars["String"]>;
version?: Maybe<Scalars["String"]>;
name: Scalars["String"];
version: Scalars["String"];
supportsPredefinedConnections?: Maybe<Scalars["Boolean"]>;
supportsProvidedConnections?: Maybe<Scalars["Boolean"]>;
supportsCustomConnections?: Maybe<Scalars["Boolean"]>;
supportsConnectionBrowser?: Maybe<Scalars["Boolean"]>;
supportsWorkspaces?: Maybe<Scalars["Boolean"]>;
supportedLanguages?: Maybe<Array<Maybe<ServerLanguage>>>;
supportedLanguages: Array<Maybe<ServerLanguage>>;
services?: Maybe<Array<Maybe<WebServiceConfig>>>;
productConfiguration: Scalars["Object"];
};
@@ -443,11 +448,11 @@ export type ServerMessage = {
export type SessionInfo = {
id: Scalars["ID"];
createTime?: Maybe<Scalars["String"]>;
lastAccessTime?: Maybe<Scalars["String"]>;
locale?: Maybe<Scalars["String"]>;
createTime: Scalars["String"];
lastAccessTime: Scalars["String"];
locale: Scalars["String"];
serverMessages?: Maybe<Array<Maybe<ServerMessage>>>;
connections?: Maybe<Array<Maybe<ConnectionInfo>>>;
connections: Array<Maybe<ConnectionInfo>>;
};
export type SqlCompletionProposal = {
@@ -562,6 +567,16 @@ export type CloseConnectionMutationVariables = {
export type CloseConnectionMutation = Pick<Mutation, "closeConnection">;
export type ConnectionStateQueryVariables = {
id: Scalars["ID"];
};
export type ConnectionStateQuery = {
connection: Maybe<
Pick<ConnectionInfo, "id" | "name" | "driverId" | "connected">
>;
};
export type CreateConnectionMutationVariables = {
config: ConnectionConfig;
};
@@ -1094,9 +1109,9 @@ export type OpenSessionMutationVariables = {};
export type OpenSessionMutation = {
session: Maybe<
Pick<SessionInfo, "locale"> & {
connections: Maybe<
Array<Maybe<Pick<ConnectionInfo, "id" | "name" | "driverId">>>
Pick<SessionInfo, "id" | "createTime" | "lastAccessTime" | "locale"> & {
connections: Array<
Maybe<Pick<ConnectionInfo, "id" | "name" | "driverId" | "connected">>
>;
}
>;
@@ -1106,9 +1121,19 @@ export type ServerConfigQueryVariables = {};
export type ServerConfigQuery = {
serverConfig: Maybe<
Pick<ServerConfig, "name" | "version"> & {
supportedLanguages: Maybe<
Array<Maybe<Pick<ServerLanguage, "isoCode" | "nativeName">>>
Pick<
ServerConfig,
| "name"
| "version"
| "productConfiguration"
| "supportsPredefinedConnections"
| "supportsProvidedConnections"
| "supportsCustomConnections"
| "supportsConnectionBrowser"
| "supportsWorkspaces"
> & {
supportedLanguages: Array<
Maybe<Pick<ServerLanguage, "isoCode" | "displayName" | "nativeName">>
>;
}
>;
@@ -1118,9 +1143,9 @@ export type SessionStateQueryVariables = {};
export type SessionStateQuery = {
sessionState: Maybe<
Pick<SessionInfo, "locale"> & {
connections: Maybe<
Array<Maybe<Pick<ConnectionInfo, "id" | "name" | "driverId">>>
Pick<SessionInfo, "id" | "createTime" | "lastAccessTime" | "locale"> & {
connections: Array<
Maybe<Pick<ConnectionInfo, "id" | "name" | "driverId" | "connected">>
>;
}
>;
@@ -1153,6 +1178,16 @@ export const CloseConnectionDocument = gql`
closeConnection(id: $id)
}
`;
export const ConnectionStateDocument = gql`
query connectionState($id: ID!) {
connection: connectionState(id: $id) {
id
name
driverId
connected
}
}
`;
export const CreateConnectionDocument = gql`
mutation createConnection($config: ConnectionConfig!) {
createConnection(config: $config) {
@@ -1618,12 +1653,16 @@ export const UpdateResultsDataDocument = gql`
export const OpenSessionDocument = gql`
mutation openSession {
session: openSession {
id
createTime
lastAccessTime
locale
connections {
id
name
driverId
connected
}
locale
}
}
`;
@@ -1632,22 +1671,34 @@ export const ServerConfigDocument = gql`
serverConfig {
name
version
productConfiguration
supportsPredefinedConnections
supportsProvidedConnections
supportsCustomConnections
supportsConnectionBrowser
supportsWorkspaces
supportedLanguages {
isoCode
displayName
nativeName
}
productConfiguration
}
}
`;
export const SessionStateDocument = gql`
query sessionState {
sessionState {
id
createTime
lastAccessTime
locale
connections {
id
name
driverId
connected
}
locale
}
}
`;
@@ -1674,6 +1725,14 @@ export function getSdk(client: GraphQLClient) {
variables,
);
},
connectionState(
variables: ConnectionStateQueryVariables,
): Promise<ConnectionStateQuery> {
return client.request<ConnectionStateQuery>(
print(ConnectionStateDocument),
variables,
);
},
createConnection(
variables: CreateConnectionMutationVariables,
): Promise<CreateConnectionMutation> {