diff --git a/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls b/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls index a0c9886833..66b3bca2fe 100644 --- a/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls +++ b/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls @@ -60,7 +60,7 @@ type ServerConfig { supportsConnectionBrowser: Boolean supportsWorkspaces: Boolean - supportedLanguages: [ ServerLanguage ]! + supportedLanguages: [ ServerLanguage! ]! services: [ WebServiceConfig ] productConfiguration: Object! } @@ -73,7 +73,7 @@ type SessionInfo { cacheExpired: Boolean! serverMessages: [ ServerMessage ] - connections: [ ConnectionInfo ]! + connections: [ ConnectionInfo! ]! } #################################################### @@ -183,7 +183,7 @@ type ConnectionInfo { clientVersion: String # Supported features (provided etc) - features: [ String! ] + features: [ String! ]! } type LogEntry { @@ -199,30 +199,30 @@ type LogEntry { extend type Query { # Returns server config - serverConfig: ServerConfig + serverConfig: ServerConfig! # Returns session state ( initialize if not ) - sessionState: SessionInfo + sessionState: SessionInfo! # Session permissions sessionPermissions: [ID]! # Get driver info - driverList( id: ID ): [ DriverInfo! ] + driverList( id: ID ): [ DriverInfo! ]! # Get list of predefined data sources - dataSourceList: [ DataSourceInfo! ] + dataSourceList: [ DataSourceInfo! ]! # Return connection state - connectionState( id: ID! ): ConnectionInfo + connectionState( id: ID! ): ConnectionInfo! - readSessionLog(maxEntries: Int, clearEntries: Boolean): [ LogEntry! ] + readSessionLog(maxEntries: Int, clearEntries: Boolean): [ LogEntry! ]! } extend type Mutation { # Initialize session - openSession: SessionInfo + openSession: SessionInfo! # Destroy session closeSession: Boolean @@ -234,13 +234,13 @@ extend type Mutation { changeSessionLanguage(locale: String): Boolean # Create new connection - createConnection( config: ConnectionConfig! ): ConnectionInfo + createConnection( config: ConnectionConfig! ): ConnectionInfo! # Test connection configuration. Returns remote server version - testConnection( config: ConnectionConfig! ): ConnectionInfo + testConnection( config: ConnectionConfig! ): ConnectionInfo! # Connect to database - openConnection( config: ConnectionConfig! ): ConnectionInfo + openConnection( config: ConnectionConfig! ): ConnectionInfo! # Disconnect from database closeConnection( id: ID! ): Boolean! diff --git a/server/bundles/io.cloudbeaver.server/schema/service.navigator.graphqls b/server/bundles/io.cloudbeaver.server/schema/service.navigator.graphqls index e8b12e7f6d..45035e32ff 100644 --- a/server/bundles/io.cloudbeaver.server/schema/service.navigator.graphqls +++ b/server/bundles/io.cloudbeaver.server/schema/service.navigator.graphqls @@ -29,7 +29,7 @@ type ObjectPropertyInfo { # Default property value defaultValue: Object # Supported features (system, hidden, inherited, foreign, expensive, etc) - features: [ String! ] + features: [ String! ]! } input ObjectPropertyFilter { @@ -94,10 +94,8 @@ type NavigatorNodeInfo { } type DatabaseStructContainers { - catalogList: [ DatabaseObjectInfo! ]! schemaList: [ DatabaseObjectInfo! ]! - } #################################################### @@ -111,9 +109,9 @@ extend type Query { parentPath: ID!, offset: Int, limit: Int, - onlyFolders: Boolean): [ NavigatorNodeInfo! ] + onlyFolders: Boolean): [ NavigatorNodeInfo! ]! - navNodeInfo( nodePath: ID! ): NavigatorNodeInfo + navNodeInfo( nodePath: ID! ): NavigatorNodeInfo! navRefreshNode( nodePath: ID! ): Boolean diff --git a/server/bundles/io.cloudbeaver.server/schema/service.sql.graphqls b/server/bundles/io.cloudbeaver.server/schema/service.sql.graphqls index d160d76418..0442ccd4a0 100644 --- a/server/bundles/io.cloudbeaver.server/schema/service.sql.graphqls +++ b/server/bundles/io.cloudbeaver.server/schema/service.sql.graphqls @@ -99,7 +99,7 @@ type SQLExecuteInfo { statusMessage: String duration: Int - results: [ SQLQueryResults! ] + results: [ SQLQueryResults! ]! } extend type Query { diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebAuthProviderInfo.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebAuthProviderInfo.java index 306b8c6f26..c80bcdb810 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebAuthProviderInfo.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebAuthProviderInfo.java @@ -17,10 +17,14 @@ package io.cloudbeaver.model.user; import io.cloudbeaver.registry.WebAuthProviderDescriptor; +import io.cloudbeaver.registry.WebAuthProviderPropertyDescriptor; +import io.cloudbeaver.server.CBPlatform; import org.jkiss.dbeaver.Log; +import java.util.List; + /** - * Web user. + * WebAuthProviderInfo. */ public class WebAuthProviderInfo { @@ -48,6 +52,14 @@ public class WebAuthProviderInfo { return descriptor.getDescription(); } + public boolean isDefaultProvider() { + return descriptor.getId().equals(CBPlatform.getInstance().getApplication().getAppConfiguration().getDefaultAuthProvider()); + } + + public List getCredentialParameters() { + return descriptor.getCredentialParameters(); + } + @Override public String toString() { return getId(); diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBAppConfig.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBAppConfig.java index bc601957f6..ea4c09a24d 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBAppConfig.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBAppConfig.java @@ -16,6 +16,8 @@ */ package io.cloudbeaver.server; +import io.cloudbeaver.auth.provider.local.LocalAuthProvider; + /** * Application configuration */ @@ -30,6 +32,7 @@ public class CBAppConfig { private boolean supportsUserWorkspaces = false; private String[] enabledDrivers = new String[0]; private String[] disabledDrivers = new String[0]; + private String defaultAuthProvider = LocalAuthProvider.PROVIDER_ID; public boolean isAuthenticationEnabled() { return authenticationEnabled; @@ -70,4 +73,8 @@ public class CBAppConfig { public String[] getDisabledDrivers() { return disabledDrivers; } + + public String getDefaultAuthProvider() { + return defaultAuthProvider; + } } diff --git a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls index 84c2756421..96c559a0bf 100644 --- a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls +++ b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls @@ -23,11 +23,11 @@ type AuthProviderInfo { icon: ID description: String - isDefault: Boolean + defaultProvider: Boolean #configurationParameters: [ObjectPropertyInfo]! - credentialParameters: [AuthCredentialInfo]! + credentialParameters: [AuthCredentialInfo!]! } type UserAuthInfo { @@ -46,12 +46,12 @@ type UserAuthInfo { extend type Query { - authLogin(provider: ID!, credentials: Object!): UserAuthInfo + authLogin(provider: ID!, credentials: Object!): UserAuthInfo! authLogout: Boolean sessionUser: UserAuthInfo - authProviders: [AuthProviderInfo]! + authProviders: [AuthProviderInfo!]! } diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java index 9b46cf4281..a425211d79 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java @@ -20,6 +20,7 @@ import io.cloudbeaver.DBWService; import io.cloudbeaver.DBWebException; import io.cloudbeaver.WebAction; import io.cloudbeaver.model.session.WebSession; +import io.cloudbeaver.model.user.WebAuthProviderInfo; import java.util.Map; @@ -36,4 +37,7 @@ public interface DBWServiceAuth extends DBWService { @WebAction(requirePermissions = {}) WebAuthInfo sessionUser(WebSession webSession) throws DBWebException; + + @WebAction(requirePermissions = {}) + WebAuthProviderInfo[] getAuthProviders(WebSession webSession); } diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java index 3a2e1f1ce4..df9f3935e5 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java @@ -17,7 +17,6 @@ package io.cloudbeaver.service.auth; import io.cloudbeaver.DBWebException; -import io.cloudbeaver.registry.WebServiceRegistry; import io.cloudbeaver.service.DBWBindingContext; import io.cloudbeaver.service.WebServiceBindingBase; import io.cloudbeaver.service.auth.impl.WebServiceAuthImpl; @@ -45,7 +44,7 @@ public class WebServiceBindingAuth extends WebServiceBindingBase return true; }) .dataFetcher("sessionUser", env -> getService(env).sessionUser(getWebSession(env))) - .dataFetcher("authProviders", env -> WebServiceRegistry.getInstance().getAuthProviders()) + .dataFetcher("authProviders", env -> getService(env).getAuthProviders(getWebSession(env))) ; } diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java index 1725eb52a4..6f9d1cbb2f 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java @@ -21,6 +21,7 @@ import io.cloudbeaver.DBWAuthProviderExternal; import io.cloudbeaver.DBWSecurityController; import io.cloudbeaver.DBWebException; import io.cloudbeaver.model.session.WebSession; +import io.cloudbeaver.model.user.WebAuthProviderInfo; import io.cloudbeaver.model.user.WebUser; import io.cloudbeaver.registry.WebAuthProviderDescriptor; import io.cloudbeaver.registry.WebServiceRegistry; @@ -126,4 +127,11 @@ public class WebServiceAuthImpl implements DBWServiceAuth { return webSession.getAttribute(ATTR_USER_AUTH); } + @Override + public WebAuthProviderInfo[] getAuthProviders(WebSession webSession) { + return WebServiceRegistry.getInstance().getAuthProviders() + .stream().map(WebAuthProviderInfo::new) + .toArray(WebAuthProviderInfo[]::new); + } + } diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/schema/service.data.transfer.graphqls b/server/bundles/io.cloudbeaver.service.data.transfer/schema/service.data.transfer.graphqls index 00aca74f33..bdf22420d2 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/schema/service.data.transfer.graphqls +++ b/server/bundles/io.cloudbeaver.service.data.transfer/schema/service.data.transfer.graphqls @@ -29,7 +29,7 @@ input DataTransferParameters { extend type Query { # Available transfer processors - dataTransferAvailableStreamProcessors: [ DataTransferProcessorInfo ] + dataTransferAvailableStreamProcessors: [ DataTransferProcessorInfo! ]! dataTransferExportDataFromContainer( connectionId: ID!, diff --git a/server/drivers/mysql/pom.xml b/server/drivers/mysql/pom.xml index 7187c27459..2a10c8421b 100644 --- a/server/drivers/mysql/pom.xml +++ b/server/drivers/mysql/pom.xml @@ -11,7 +11,7 @@ - mysql + mysql/mysql8 diff --git a/server/product/web-server/CloudbeaverServer.product.launch b/server/product/web-server/CloudbeaverServer.product.launch index 4ef2d2d892..954d8d4a80 100644 --- a/server/product/web-server/CloudbeaverServer.product.launch +++ b/server/product/web-server/CloudbeaverServer.product.launch @@ -34,11 +34,11 @@ - - + + - + diff --git a/webapp/.eslintrc.js b/webapp/.eslintrc.js index ce8712d16d..6ea94b8645 100644 --- a/webapp/.eslintrc.js +++ b/webapp/.eslintrc.js @@ -5,15 +5,12 @@ module.exports = { 'eslint:recommended', 'plugin:react/recommended', 'plugin:@typescript-eslint/recommended', - // 'plugin:prettier/recommended', - // 'prettier/@typescript-eslint', ], plugins: [ 'import', '@typescript-eslint', 'eslint-plugin-import-helpers', - // 'prettier', 'jest', ], @@ -37,7 +34,6 @@ module.exports = { }, rules: { - // 'prettier/prettier': 'warn', 'arrow-parens': ['error', 'as-needed', { 'requireForBlockBody': true }], 'curly': ['error', 'all'], 'class-methods-use-this': 'off', @@ -76,12 +72,15 @@ module.exports = { 'no-restricted-globals': 'off', 'no-useless-constructor': 'off', 'max-classes-per-file': 'off', + 'arrow-body-style': ['error', 'as-needed'], + 'function-call-argument-newline': ['error', 'consistent'], 'max-len': [ 'error', { code: 120, ignoreTrailingComments: true, - ignoreStrings: true + ignoreStrings: true, + ignoreTemplateLiterals: true } ], 'lines-between-class-members': 'off', diff --git a/webapp/.prettierrc.js b/webapp/.prettierrc.js deleted file mode 100644 index 515be6cdc5..0000000000 --- a/webapp/.prettierrc.js +++ /dev/null @@ -1,9 +0,0 @@ -// we use prettier only for graphQL-codegen - -module.exports = { - useTabs: false, - tabWidth: 2, - trailingComma: 'all', - singleQuote: false, - semi: true, -} diff --git a/webapp/package.json b/webapp/package.json index e3f92311d4..c7c73b0985 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -33,7 +33,6 @@ "@typescript-eslint/parser": "^2.8.0", "eslint": "^6.6.0", "eslint-config-airbnb-base": "^14.0.0", - "eslint-config-prettier": "^6.5.0", "eslint-import-resolver-typescript": "^2.0.0", "eslint-loader": "^3.0.3", "eslint-plugin-import": "^2.18.2", diff --git a/webapp/packages/_plugin-template/package.json b/webapp/packages/_plugin-template/package.json index ddf90b3971..49df72c7b7 100644 --- a/webapp/packages/_plugin-template/package.json +++ b/webapp/packages/_plugin-template/package.json @@ -15,14 +15,6 @@ }, "peerDependencies": { "@dbeaver/core": "^0.1.0", - "@material/button": "^4.0.0", - "@material/elevation": "^4.0.0", - "@material/ripple": "^4.0.0", - "@material/theme": "^4.0.0", - "@material/typography": "^4.0.0", - "graphql": "^14.5.8", - "graphql-request": "^1.8.2", - "graphql-tag": "^2.10.1", "mobx": "^5.15.4", "mobx-react": "^6.2.2", "react": "^16.13.1", diff --git a/webapp/packages/ag-grid-plugin/package.json b/webapp/packages/ag-grid-plugin/package.json index 859c7523f2..f99c9a8c12 100644 --- a/webapp/packages/ag-grid-plugin/package.json +++ b/webapp/packages/ag-grid-plugin/package.json @@ -22,14 +22,6 @@ }, "peerDependencies": { "@dbeaver/core": "^0.1.0", - "@material/button": "^4.0.0", - "@material/elevation": "^4.0.0", - "@material/ripple": "^4.0.0", - "@material/theme": "^4.0.0", - "@material/typography": "^4.0.0", - "graphql": "^14.5.8", - "graphql-request": "^1.8.2", - "graphql-tag": "^2.10.1", "mobx": "^5.15.4", "mobx-react": "^6.2.2", "react": "^16.13.1", diff --git a/webapp/packages/authentication/.gitignore b/webapp/packages/authentication/.gitignore new file mode 100644 index 0000000000..d443c6b30b --- /dev/null +++ b/webapp/packages/authentication/.gitignore @@ -0,0 +1,18 @@ + +# dependencies +/node_modules + +# testing +/coverage + +# production +/dist + +# misc +.DS_Store +.env* + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/webapp/packages/authentication/package.json b/webapp/packages/authentication/package.json new file mode 100644 index 0000000000..08642d2e96 --- /dev/null +++ b/webapp/packages/authentication/package.json @@ -0,0 +1,24 @@ +{ + "name": "@dbeaver/authentication", + "version": "0.1.0", + "description": "", + "license": "Apache-2.0", + "main": "dist/index.js", + "types": "dist/types", + "scripts": { + "build-plugin": "npm run clean-dist && webpack -p --config=../../configs/webpack/build-plugin.js", + "clean-dist": "rimraf dist/*", + "rollup": "npm run clean-dist && rollup --config ../../configs/rollup.config.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "peerDependencies": { + "@dbeaver/core": "^0.1.0", + "mobx": "^5.15.4", + "mobx-react": "^6.2.2", + "react": "^16.13.1", + "react-dom": "^16.13.1", + "reakit": "^1.0.0-beta.11", + "reshadow": "0.0.1-alpha.75", + "rxjs": "^6.5.5" + } +} diff --git a/webapp/packages/authentication/src/AuthInfoService.ts b/webapp/packages/authentication/src/AuthInfoService.ts new file mode 100644 index 0000000000..40fe4d55ff --- /dev/null +++ b/webapp/packages/authentication/src/AuthInfoService.ts @@ -0,0 +1,72 @@ +/* + * 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 { observable } from 'mobx'; + +import { injectable } from '@dbeaver/core/di'; +import { NotificationService } from '@dbeaver/core/eventsLog'; +import { SessionService } from '@dbeaver/core/root'; +import { GraphQLService, UserAuthInfo } from '@dbeaver/core/sdk'; + +import { AuthProviderService } from './AuthProviderService'; + +@injectable() +export class AuthInfoService { + @observable private user: UserAuthInfo | null = null; + + constructor( + private graphQLService: GraphQLService, + private notificationService: NotificationService, + private authProviderService: AuthProviderService, + private sessionService: SessionService, + ) { } + + get userInfo() { + return this.user; + } + + async login(provider: string, credentials: object): Promise { + if (this.user) { + throw new Error('User already logged in'); + } + + const processedCredentials = this.authProviderService.processCredentials(provider, credentials); + + const { user } = await this.graphQLService.gql.authLogin({ + provider, + credentials: processedCredentials, + }); + this.user = user; + + await this.updateSession(); + return this.user; + } + + async logout() { + if (this.user) { + await this.graphQLService.gql.authLogout(); + this.user = null; + await this.updateSession(); + } + } + + async updateAuthInfo(): Promise { + try { + const { user } = await this.graphQLService.gql.getSessionUser(); + this.user = user || null; + } catch (exception) { + this.notificationService.logException(exception, 'Can\'t load session user'); + } + + return this.user; + } + + private async updateSession() { + await this.sessionService.update(); + } +} diff --git a/webapp/packages/authentication/src/AuthMenuService.ts b/webapp/packages/authentication/src/AuthMenuService.ts new file mode 100644 index 0000000000..0ed1ee92a4 --- /dev/null +++ b/webapp/packages/authentication/src/AuthMenuService.ts @@ -0,0 +1,59 @@ +/* + * 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 { SettingsMenuService, TopNavService } from '@dbeaver/core/app'; +import { injectable } from '@dbeaver/core/di'; +import { ServerService } from '@dbeaver/core/root'; + +import { AuthenticationService } from './AuthenticationService'; +import { AuthInfoService } from './AuthInfoService'; +import { AuthDialogService } from './Dialog/AuthDialogService'; +import { UserInfo } from './UserInfo'; + +@injectable() +export class AuthMenuService { + constructor( + private serverService: ServerService, + private authDialogService: AuthDialogService, + private authInfoService: AuthInfoService, + private settingsMenuService: SettingsMenuService, + private authenticationService: AuthenticationService, + private topNavService: TopNavService, + ) { } + + register() { + this.settingsMenuService.addMenuItem( + SettingsMenuService.settingsMenuToken, + { + id: 'login', + order: 0, + isHidden: () => !this.serverService.config.data?.authenticationEnabled || !!this.authInfoService.userInfo, + title: 'authentication_login', + onClick: () => this.authDialogService.showLoginForm(), + } + ); + + this.settingsMenuService.addMenuItem( + SettingsMenuService.settingsMenuToken, + { + id: 'logout', + order: Number.MAX_SAFE_INTEGER, + isHidden: () => !this.serverService.config.data?.authenticationEnabled || !this.authInfoService.userInfo, + title: 'authentication_logout', + onClick: this.logout.bind(this), + } + ); + + this.topNavService.placeholder.add(UserInfo, 4); + } + + private async logout() { + await this.authInfoService.logout(); + await this.authenticationService.auth(); + } +} diff --git a/webapp/packages/authentication/src/AuthProviderService.ts b/webapp/packages/authentication/src/AuthProviderService.ts new file mode 100644 index 0000000000..9d9bd18ff7 --- /dev/null +++ b/webapp/packages/authentication/src/AuthProviderService.ts @@ -0,0 +1,52 @@ +/* + * 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 { createHash } from 'crypto'; + +import { injectable } from '@dbeaver/core/di'; +import { + GraphQLService, CachedResource, AuthProviderInfo +} from '@dbeaver/core/sdk'; + +export type AuthProvider = Omit + +@injectable() +export class AuthProviderService { + readonly providers = new CachedResource([], this.refreshAsync.bind(this), data => !!data.length) + + constructor( + private graphQLService: GraphQLService, + ) { } + + processCredentials(providerId: string, credentials: any) { + const provider = this.providers.data.find(provider => provider.id === providerId); + + if (!provider) { + return credentials; + } + + const credentialsProcessed = { ...credentials }; + for (const parameter of provider.credentialParameters) { + if (parameter.encryption === 'hash' && parameter.id in credentialsProcessed) { + const md5Hash = createHash('md5') + .update(credentialsProcessed[parameter.id]) + .digest('hex') + .toUpperCase(); + credentialsProcessed[parameter.id] = md5Hash; + } + } + + return credentialsProcessed; + } + + private async refreshAsync(data: AuthProvider[]): Promise { + const { providers } = await this.graphQLService.gql.getAuthProviders(); + + return providers; + } +} diff --git a/webapp/packages/authentication/src/AuthenticationService.ts b/webapp/packages/authentication/src/AuthenticationService.ts new file mode 100644 index 0000000000..7ba584a671 --- /dev/null +++ b/webapp/packages/authentication/src/AuthenticationService.ts @@ -0,0 +1,42 @@ +/* + * 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 { AuthInfoService } from './AuthInfoService'; +import { AuthDialogService } from './Dialog/AuthDialogService'; + +@injectable() +export class AuthenticationService { + constructor( + private serverService: ServerService, + private authDialogService: AuthDialogService, + private authInfoService: AuthInfoService, + ) { } + + async auth() { + const config = await this.serverService.config.load(); + if (!config) { + throw new Error('Can\'t configure Authentication'); + } + + if (!config.authenticationEnabled) { + return; + } + + const userInfo = await this.authInfoService.updateAuthInfo(); + if (userInfo) { + return; + } + + if (!config.anonymousAccessEnabled) { + await this.authDialogService.showLoginForm(true); + } + } +} diff --git a/webapp/packages/authentication/src/Bootstrap.ts b/webapp/packages/authentication/src/Bootstrap.ts new file mode 100644 index 0000000000..7069db937d --- /dev/null +++ b/webapp/packages/authentication/src/Bootstrap.ts @@ -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 { AuthenticationService } from './AuthenticationService'; +import { AuthMenuService } from './AuthMenuService'; + +@injectable() +export class Bootstrap { + constructor( + private authenticationService: AuthenticationService, + private authMenuService: AuthMenuService + ) { } + + async bootstrap() { + this.authMenuService.register(); + await this.authenticationService.auth(); + } +} diff --git a/webapp/packages/authentication/src/Dialog/AuthDialog.tsx b/webapp/packages/authentication/src/Dialog/AuthDialog.tsx new file mode 100644 index 0000000000..202f3c5c85 --- /dev/null +++ b/webapp/packages/authentication/src/Dialog/AuthDialog.tsx @@ -0,0 +1,134 @@ +/* + * 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 { observer } from 'mobx-react'; +import styled, { css } from 'reshadow'; + +import { + SubmittingForm, ErrorMessage, TabsState, TabList, Tab, TabTitle +} from '@dbeaver/core/blocks'; +import { useController } from '@dbeaver/core/di'; +import { DialogComponent, CommonDialogWrapper } from '@dbeaver/core/dialogs'; +import { useTranslate } from '@dbeaver/core/localization'; +import { composes, useStyles } from '@dbeaver/core/theming'; + +import { AuthDialogController } from './AuthDialogController'; +import { AuthDialogFooter } from './AuthDialogFooter'; +import { AuthProviderForm } from './AuthProviderForm/AuthProviderForm'; + +const styles = composes( + css` + Tab { + composes: theme-ripple theme-background-secondary theme-text-on-secondary from global; + } + ErrorMessage { + composes: theme-background-secondary from global; + } + `, + css` + custom-connection { + display: flex; + flex-direction: column; + box-sizing: border-box; + } + CommonDialogWrapper { + min-height: 400px; + min-width: 600px; + } + SubmittingForm { + overflow: auto; + margin: auto; + margin-top: 20px; + margin-bottom: 20px; + } + SubmittingForm, AuthProviderForm { + flex: 1; + display: flex; + flex-direction: column; + } + + TabList { + box-sizing: border-box; + display: inline-flex; + width: 100%; + padding-left: 24px; + } + Tab { + composes: theme-typography--body2 from global; + text-transform: uppercase; + font-weight: normal; + + &:global([aria-selected=true]) { + font-weight: normal !important; + } + } + AuthProviderForm { + flex-direction: column; + padding: 18px 24px; + } + ErrorMessage { + position: sticky; + bottom: 0; + padding: 8px 24px; + } + ` +); + +export const AuthDialog: DialogComponent = observer( + function AuthDialog(props) { + const controller = useController(AuthDialogController, props.rejectDialog); + const translate = useTranslate(); + + return styled(useStyles(styles))( + + + {controller.providers.map(provider => ( + controller.selectProvider(provider.id)} + > + + + ))} + + )} + footer={( + + )} + onReject={props.options?.persistent ? undefined : props.rejectDialog} + > + + {controller.provider && ( + + )} + {!controller.provider && <>Select available provider} + + {controller.error.responseMessage && ( + + )} + + + ); + } +); diff --git a/webapp/packages/authentication/src/Dialog/AuthDialogController.ts b/webapp/packages/authentication/src/Dialog/AuthDialogController.ts new file mode 100644 index 0000000000..ba86ba4a20 --- /dev/null +++ b/webapp/packages/authentication/src/Dialog/AuthDialogController.ts @@ -0,0 +1,113 @@ +/* + * 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 { observable, computed } from 'mobx'; + +import { injectable, IInitializableController, IDestructibleController } from '@dbeaver/core/di'; +import { CommonDialogService } from '@dbeaver/core/dialogs'; +import { NotificationService } from '@dbeaver/core/eventsLog'; +import { GQLErrorCatcher } from '@dbeaver/core/sdk'; +import { ErrorDetailsDialog } from '@dbeaver/core/src/app'; + +import { AuthInfoService } from '../AuthInfoService'; +import { AuthProviderService, AuthProvider } from '../AuthProviderService'; + +@injectable() +export class AuthDialogController implements IInitializableController, IDestructibleController { + @observable provider: AuthProvider | null = null + @observable isAuthenticating = false; + @observable credentials = {}; + + get isLoading() { + return this.authProviderService.providers.isLoading(); + } + + @computed get providers(): AuthProvider[] { + return this.authProviderService + .providers + .data + .concat() + .sort(this.compareProviders); + } + + readonly error = new GQLErrorCatcher(); + private isDistructed = false; + private close!: () => void; + + constructor( + private notificationService: NotificationService, + private authProviderService: AuthProviderService, + private authInfoService: AuthInfoService, + private commonDialogService: CommonDialogService, + ) { } + + init(onClose: () => void) { + this.close = onClose; + this.loadProviders(); + } + + destruct(): void { + this.isDistructed = true; + } + + login = async () => { + if (!this.provider || this.isAuthenticating) { + return; + } + + this.isAuthenticating = true; + try { + await this.authInfoService.login(this.provider.id, this.credentials); + this.close(); + } catch (exception) { + if (!this.error.catch(exception) || this.isDistructed) { + this.notificationService.logException(exception, 'Login failed'); + } + } finally { + this.isAuthenticating = false; + } + } + + selectProvider = (providerId: string) => { + if (providerId === this.provider?.id) { + return; + } + this.provider = this.authProviderService + .providers.data.find(provider => provider.id === providerId) || null; + this.credentials = {}; + } + + showDetails = () => { + if (this.error.exception) { + this.commonDialogService.open(ErrorDetailsDialog, this.error.exception); + } + } + + private async loadProviders() { + try { + await this.authProviderService.providers.load(); + if (this.providers.length > 0) { + this.provider = this.providers[0]; + } + } catch (exception) { + this.notificationService.logException(exception, 'Can\'t load auth providers'); + } + } + + private compareProviders = (providerA: AuthProvider, providerB: AuthProvider): number => { + if (providerA.defaultProvider === providerB.defaultProvider) + { + return providerA.label.localeCompare(providerB.label); + } + + if (providerA.defaultProvider) { + return -1; + } + return 1; + } +} diff --git a/webapp/packages/authentication/src/Dialog/AuthDialogFooter.tsx b/webapp/packages/authentication/src/Dialog/AuthDialogFooter.tsx new file mode 100644 index 0000000000..cad62287d3 --- /dev/null +++ b/webapp/packages/authentication/src/Dialog/AuthDialogFooter.tsx @@ -0,0 +1,51 @@ +/* + * 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 { observer } from 'mobx-react'; +import styled, { css } from 'reshadow'; + +import { Button } from '@dbeaver/core/blocks'; +import { useTranslate } from '@dbeaver/core/localization'; + +const styles = css` + controls { + display: flex; + height: 100%; + flex: 1; + align-items: center; + margin: auto; + justify-content: flex-end; + } +`; + +export type Props = { + isAuthenticating: boolean; + onLogin(): void; +} + +export const AuthDialogFooter = observer( + function AuthDialogFooter({ + isAuthenticating, + onLogin, + }: Props) { + const translate = useTranslate(); + + return styled(styles)( + + + + ); + } +); diff --git a/webapp/packages/authentication/src/Dialog/AuthDialogService.ts b/webapp/packages/authentication/src/Dialog/AuthDialogService.ts new file mode 100644 index 0000000000..398928f594 --- /dev/null +++ b/webapp/packages/authentication/src/Dialog/AuthDialogService.ts @@ -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 { CommonDialogService } from '@dbeaver/core/dialogs'; + +import { AuthDialog } from './AuthDialog'; + +@injectable() +export class AuthDialogService { + constructor( + private commonDialogService: CommonDialogService, + ) { } + + async showLoginForm(persistent = false) { + await this.commonDialogService.open(AuthDialog, null, { persistent }); + } +} diff --git a/webapp/packages/authentication/src/Dialog/AuthProviderForm/AuthProviderForm.tsx b/webapp/packages/authentication/src/Dialog/AuthProviderForm/AuthProviderForm.tsx new file mode 100644 index 0000000000..c67d609472 --- /dev/null +++ b/webapp/packages/authentication/src/Dialog/AuthProviderForm/AuthProviderForm.tsx @@ -0,0 +1,52 @@ +/* + * 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 { observer } from 'mobx-react'; +import { useCallback } from 'react'; +import styled from 'reshadow'; + +import { InputField } from '@dbeaver/core/blocks'; +import { useStyles } from '@dbeaver/core/theming'; + +import { AuthProvider } from '../../AuthProviderService'; +import { formStyles } from './formStyles'; + +type Props = { + provider: AuthProvider; + credentials: any; + authenticate: boolean; +} + +export const AuthProviderForm = observer(function AuthProviderForm({ + provider, + credentials, + authenticate, +}: Props) { + const handleChange = useCallback((key: string, value: string) => { + credentials[key] = value; + }, [credentials]); + + return styled(useStyles(formStyles))( + + {provider.credentialParameters.map(parameter => parameter.user && ( + + handleChange(parameter.id, value)} + disabled={authenticate} + mod='surface' + > + {parameter.displayName} + + + ))} + + ); +}); diff --git a/webapp/packages/authentication/src/Dialog/AuthProviderForm/formStyles.ts b/webapp/packages/authentication/src/Dialog/AuthProviderForm/formStyles.ts new file mode 100644 index 0000000000..4e04e391da --- /dev/null +++ b/webapp/packages/authentication/src/Dialog/AuthProviderForm/formStyles.ts @@ -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 { css } from 'reshadow'; + +export const formStyles = css` + login-form { + flex: 1; + box-sizing: border-box; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + group { + box-sizing: border-box; + display: flex; + margin: 0 12px; + } +`; diff --git a/webapp/packages/authentication/src/UserInfo.tsx b/webapp/packages/authentication/src/UserInfo.tsx new file mode 100644 index 0000000000..dfabf6b880 --- /dev/null +++ b/webapp/packages/authentication/src/UserInfo.tsx @@ -0,0 +1,58 @@ +/* + * 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 { observer } from 'mobx-react'; +import styled, { css } from 'reshadow'; + +import { IconOrImage } from '@dbeaver/core/blocks'; +import { useService } from '@dbeaver/core/di'; +import { composes, useStyles } from '@dbeaver/core/theming'; + +import { AuthInfoService } from './AuthInfoService'; + +const styles = composes( + css` + user { + composes: theme-ripple from global; + } + `, + css` + user { + height: 100%; + padding: 0 16px; + display: flex; + align-items: center; + } + IconOrImage { + display: block; + width: 24px; + } + user-name { + display: block; + line-height: initial; + margin-left: 8px; + } + ` +); + +export const UserInfo = observer(function UserInfo() { + const authInfoService = useService(AuthInfoService); + + if (!authInfoService.userInfo) { + return null; + } + + return styled(useStyles(styles))( + + + + + {authInfoService.userInfo.userId} + + ); +}); diff --git a/webapp/packages/authentication/src/index.ts b/webapp/packages/authentication/src/index.ts new file mode 100644 index 0000000000..86b1aba5f9 --- /dev/null +++ b/webapp/packages/authentication/src/index.ts @@ -0,0 +1,8 @@ +import { manifest } from './manifest'; + +export default manifest; + +export * from './AuthenticationService'; +export * from './AuthInfoService'; +export * from './AuthProviderService'; +export * from './Dialog/AuthDialogService'; diff --git a/webapp/packages/authentication/src/manifest.ts b/webapp/packages/authentication/src/manifest.ts new file mode 100644 index 0000000000..b70a6a4e50 --- /dev/null +++ b/webapp/packages/authentication/src/manifest.ts @@ -0,0 +1,36 @@ +/* + * 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 { IServiceInjector, PluginManifest } from '@dbeaver/core/di'; + +import { AuthenticationService } from './AuthenticationService'; +import { AuthInfoService } from './AuthInfoService'; +import { AuthMenuService } from './AuthMenuService'; +import { AuthProviderService } from './AuthProviderService'; +import { Bootstrap } from './Bootstrap'; +import { AuthDialogService } from './Dialog/AuthDialogService'; + +export const manifest: PluginManifest = { + info: { + name: 'Authentication', + }, + + providers: [ + AuthenticationService, + AuthInfoService, + AuthProviderService, + AuthDialogService, + AuthMenuService, + ], + + async initialize(services: IServiceInjector) { + services + .resolveServiceByClass(Bootstrap) + .bootstrap(); + }, +}; diff --git a/webapp/packages/authentication/tsconfig.build.json b/webapp/packages/authentication/tsconfig.build.json new file mode 100644 index 0000000000..dd700023ea --- /dev/null +++ b/webapp/packages/authentication/tsconfig.build.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "declarationDir": "dist/types", + "declaration": true, + "noEmit": false, + "outDir": "dist/js" + }, + "exclude": [ + "node_modules", + "@dbeaver/**" + ], + "include": ["src/**/*"] +} diff --git a/webapp/packages/authentication/tsconfig.json b/webapp/packages/authentication/tsconfig.json new file mode 100644 index 0000000000..cf0afdc7c0 --- /dev/null +++ b/webapp/packages/authentication/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "@dbeaver/**" + ] +} diff --git a/webapp/packages/basic-connection-plugin/package.json b/webapp/packages/basic-connection-plugin/package.json index 5a0ad54716..d84400b9d2 100644 --- a/webapp/packages/basic-connection-plugin/package.json +++ b/webapp/packages/basic-connection-plugin/package.json @@ -17,14 +17,6 @@ }, "peerDependencies": { "@dbeaver/core": "^0.1.0", - "@material/button": "^4.0.0", - "@material/elevation": "^4.0.0", - "@material/ripple": "^4.0.0", - "@material/theme": "^4.0.0", - "@material/typography": "^4.0.0", - "graphql": "^14.5.8", - "graphql-request": "^1.8.2", - "graphql-tag": "^2.10.1", "mobx": "^5.15.4", "mobx-react": "^6.2.2", "react": "^16.13.1", diff --git a/webapp/packages/basic-connection-plugin/src/BasicConnectionPluginBootstrap.ts b/webapp/packages/basic-connection-plugin/src/BasicConnectionPluginBootstrap.ts index 00b684a74c..f78db82df1 100644 --- a/webapp/packages/basic-connection-plugin/src/BasicConnectionPluginBootstrap.ts +++ b/webapp/packages/basic-connection-plugin/src/BasicConnectionPluginBootstrap.ts @@ -10,6 +10,7 @@ import { MainMenuService, ConnectionDialogsService } from '@dbeaver/core/app'; import { injectable } from '@dbeaver/core/di'; import { CommonDialogService } from '@dbeaver/core/dialogs'; import { NotificationService } from '@dbeaver/core/eventsLog'; +import { PermissionsService, EPermission } from '@dbeaver/core/root'; import { BasicConnectionService } from './BasicConnectionService'; import { ConnectionDialog } from './ConnectionDialog/ConnectionDialog'; @@ -17,13 +18,14 @@ import { ConnectionDialog } from './ConnectionDialog/ConnectionDialog'; @injectable() export class BasicConnectionPluginBootstrap { - private hasPreconfiguredConnection = false; - - constructor(private connectionDialogsService: ConnectionDialogsService, - private mainMenuService: MainMenuService, - private basicConnectionService: BasicConnectionService, - private notificationService: NotificationService, - private commonDialogService: CommonDialogService) { + constructor( + private connectionDialogsService: ConnectionDialogsService, + private mainMenuService: MainMenuService, + private basicConnectionService: BasicConnectionService, + private commonDialogService: CommonDialogService, + private notificationService: NotificationService, + private permissionsService: PermissionsService + ) { } bootstrap() { @@ -33,9 +35,10 @@ export class BasicConnectionPluginBootstrap { { id: 'mainMenuConnect', order: 2, - title: 'Preconfigured', + title: 'basicConnection_main_menu_item', onClick: () => this.openConnectionsDialog(), - isDisabled: () => !this.hasPreconfiguredConnection, + isHidden: () => !this.permissionsService.has(EPermission.public), + isDisabled: () => !this.basicConnectionService.dbSources.data.length, } ); } @@ -46,11 +49,9 @@ export class BasicConnectionPluginBootstrap { private async loadDbSources() { try { - const sources = await this.basicConnectionService.loadDBSourcesAsync(); - this.hasPreconfiguredConnection = !!sources.length; + await this.basicConnectionService.dbSources.load(); } catch (error) { this.notificationService.logException(error, 'DBSources loading failed'); } } - } diff --git a/webapp/packages/basic-connection-plugin/src/BasicConnectionService.ts b/webapp/packages/basic-connection-plugin/src/BasicConnectionService.ts index 2836903fd3..74e20577dd 100644 --- a/webapp/packages/basic-connection-plugin/src/BasicConnectionService.ts +++ b/webapp/packages/basic-connection-plugin/src/BasicConnectionService.ts @@ -6,30 +6,35 @@ * you may not use this file except in compliance with the License. */ -import { Connection, DBSource, ConnectionsManagerService } from '@dbeaver/core/app'; +import { + Connection, DBSource, ConnectionsManagerService +} from '@dbeaver/core/app'; import { injectable } from '@dbeaver/core/di'; -import { ConnectionConfig, GraphQLService } from '@dbeaver/core/sdk'; +import { PermissionsService, EPermission } from '@dbeaver/core/root'; +import { ConnectionConfig, GraphQLService, CachedResource } from '@dbeaver/core/sdk'; @injectable() export class BasicConnectionService { - private dbSourcesCache: DBSource[] = []; + readonly dbSources = new CachedResource([], this.loadDBSourcesAsync.bind(this), data => !!data.length) - constructor(private graphQLService: GraphQLService, - private connectionsManagerService: ConnectionsManagerService) { + constructor( + private graphQLService: GraphQLService, + private connectionsManagerService: ConnectionsManagerService, + private permissionsService: PermissionsService + ) { + this.permissionsService.onUpdate.subscribe(() => this.dbSources.refresh()); } public getDBSources(): DBSource[] { - return this.dbSourcesCache; + return this.dbSources.data; } - async loadDBSourcesAsync(): Promise { - if (this.dbSourcesCache.length > 0) { - return this.dbSourcesCache; + private async loadDBSourcesAsync(data: DBSource[]): Promise { + if (!await this.permissionsService.hasAsync(EPermission.public)) { + return []; } - const { dataSourceList } = await this.graphQLService.gql.dataSourceList(); - this.dbSourcesCache = dataSourceList; - return this.dbSourcesCache; + return dataSourceList; } async openConnectionAsync(config: ConnectionConfig): Promise { diff --git a/webapp/packages/basic-connection-plugin/src/ConnectionDialog/ConnectionController.ts b/webapp/packages/basic-connection-plugin/src/ConnectionDialog/ConnectionController.ts index 728735a716..7877364247 100644 --- a/webapp/packages/basic-connection-plugin/src/ConnectionDialog/ConnectionController.ts +++ b/webapp/packages/basic-connection-plugin/src/ConnectionDialog/ConnectionController.ts @@ -153,7 +153,7 @@ implements IInitializableController, IDestructibleController, IConnectionControl private async loadDBSources() { try { - await this.basicConnectionService.loadDBSourcesAsync(); + await this.basicConnectionService.dbSources.load(); await this.connectionsManagerService.loadDriversAsync(); } catch (exception) { this.notificationService.logException(exception, 'Can\'t load database sources'); diff --git a/webapp/packages/basic-connection-plugin/src/ConnectionDialog/ConnectionDialog.tsx b/webapp/packages/basic-connection-plugin/src/ConnectionDialog/ConnectionDialog.tsx index 15c0ab96c5..6232e89678 100644 --- a/webapp/packages/basic-connection-plugin/src/ConnectionDialog/ConnectionDialog.tsx +++ b/webapp/packages/basic-connection-plugin/src/ConnectionDialog/ConnectionDialog.tsx @@ -22,10 +22,8 @@ import { DBSourceSelector } from './DBSourceSelector/DBSourceSelector'; const styles = css` CommonDialogWrapper { - display: flex; - flex-direction: column; - max-height: 330px; - min-height: 330px; + max-height: 500px; + min-height: 500px; } SubmittingForm, center { display: flex; diff --git a/webapp/packages/basic-connection-plugin/src/ConnectionDialog/ConnectionDialogFooter.tsx b/webapp/packages/basic-connection-plugin/src/ConnectionDialog/ConnectionDialogFooter.tsx index 06f941c495..3805a850d8 100644 --- a/webapp/packages/basic-connection-plugin/src/ConnectionDialog/ConnectionDialogFooter.tsx +++ b/webapp/packages/basic-connection-plugin/src/ConnectionDialog/ConnectionDialogFooter.tsx @@ -15,8 +15,10 @@ import { useTranslate } from '@dbeaver/core/localization'; const styles = css` controls { display: flex; + flex: 1; height: 100%; align-items: center; + margin: auto; } fill { diff --git a/webapp/packages/builder/configs/webpack/common.js b/webapp/packages/builder/configs/webpack/common.js index 3764df2157..5cf73e28e3 100644 --- a/webapp/packages/builder/configs/webpack/common.js +++ b/webapp/packages/builder/configs/webpack/common.js @@ -50,10 +50,6 @@ module.exports = (env, argv) => merge(commonConfig(env, argv), { "@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.resolve(__dirname, '../../src/fix-gql.js'), - graphql: path.resolve(__dirname, '../../src/fix-gql.js'), - react: 'preact/compat', react$: 'preact/compat', 'react-dom': 'preact/compat', diff --git a/webapp/packages/builder/src/fix-gql.js b/webapp/packages/builder/src/fix-gql.js deleted file mode 100644 index 5f2d0259de..0000000000 --- a/webapp/packages/builder/src/fix-gql.js +++ /dev/null @@ -1,5 +0,0 @@ -/** - * rewrite imports for unnecessary work in `webapp/packages/core/src/libs/sdk.ts` - */ -module.exports = strings => strings[0] -module.exports.print = v => v diff --git a/webapp/packages/core/assets/locales/en.js b/webapp/packages/core/assets/locales/en.js index d2221efead..fcdb744f45 100644 --- a/webapp/packages/core/assets/locales/en.js +++ b/webapp/packages/core/assets/locales/en.js @@ -12,6 +12,7 @@ module.exports = { basicConnection_connectionDialog_connecting: 'Connecting...', basicConnection_connectionDialog_connect: 'Connect', basicConnection_connectionDialog_connecting_message: 'Connecting to database...', + basicConnection_main_menu_item: 'Preconfigured', customConnection_connectionType_custom: 'Parameters', customConnection_connectionType_url: 'URL', @@ -42,6 +43,10 @@ module.exports = { administration_menu_enter: 'Administration', + authentication_login_dialog_title: 'Authentication', + authentication_login: 'Login', + authentication_logout: 'Logout', + ui_stepper_back: 'Back', ui_processing_loading: 'Loading...', ui_processing_cancel: 'Cancel', diff --git a/webapp/packages/core/assets/locales/ru.js b/webapp/packages/core/assets/locales/ru.js index edc4b8362c..4f5a6ed5db 100644 --- a/webapp/packages/core/assets/locales/ru.js +++ b/webapp/packages/core/assets/locales/ru.js @@ -11,6 +11,7 @@ module.exports = { basicConnection_connectionDialog_connecting: 'Подключение...', basicConnection_connectionDialog_connect: 'Подключиться', basicConnection_connectionDialog_connecting_message: 'Подключение к базе...', + basicConnection_main_menu_item: 'Подключиться', customConnection_connectionType_custom: 'Параметры', customConnection_connectionType_url: 'URL', @@ -41,6 +42,10 @@ module.exports = { administration_menu_enter: 'Администрирование', + authentication_login_dialog_title: 'Авторизация', + authentication_login: 'Войти', + authentication_logout: 'Выйти', + ui_stepper_back: 'Назад', ui_processing_loading: 'Загрузка...', ui_processing_cancel: 'Отменить', diff --git a/webapp/packages/core/assets/styles/_tabs.scss b/webapp/packages/core/assets/styles/_tabs.scss index 2cc8c26e4a..f5cdae2e35 100644 --- a/webapp/packages/core/assets/styles/_tabs.scss +++ b/webapp/packages/core/assets/styles/_tabs.scss @@ -36,19 +36,15 @@ .__Tab { composes: theme-typography--body2 from global; position: relative; - display: flex; - flex-shrink: 0; - text-align: left; - align-items: center; outline: none; font-weight: normal; cursor: pointer; + padding: 0; height: 48px; - padding: 0 24px 0 12px; border-top: solid 2px transparent; - &:global([aria-selected=true]) { + &:global([aria-selected="true"]) { font-weight: 500; cursor: auto; border-top-color: $color-negative; @@ -58,52 +54,64 @@ } } - &:not(:global([aria-selected=true])) { + &:not(:global([aria-selected="true"])) { background-color: transparent !important; } } - tab-icon { - height: 22px; - padding-right: 8px; + tab-container { + height: 100%; + display: flex; + flex-shrink: 0; + text-align: left; + align-items: center; - & StaticImage, - & img { - width: 22px; - } - - & placeholder { - width: 22px; + tab-icon { height: 22px; + padding-left: 12px; + + StaticImage, + img { + width: 22px; + } + + placeholder { + width: 22px; + height: 22px; + } } - } - tab-title placeholder { - width: 80px; - height: 16px; - } + tab-title { + padding: 0 12px; + } - tab-action:before { - content: ""; - display: block; - left: -2px; - top: -2px; - height: 12px; - width: 12px; - position: absolute; - cursor: pointer; - } + tab-title placeholder { + width: 80px; + height: 16px; + } - tab-action { - height: 8px; - width: 8px; - align-self: flex-start; - position: absolute; - top: 4px; - right: 4px; - - & Icon { + tab-action:before { + content: ""; display: block; + left: -4px; + top: 0; + height: 16px; + width: 16px; + position: absolute; + cursor: pointer; + } + + tab-action { + position: relative; + height: 8px; + width: 8px; + align-self: flex-start; + padding: 4px; + padding-left: 0; + + Icon { + display: block; + } } } diff --git a/webapp/packages/core/codegen.yml b/webapp/packages/core/codegen.yml index e97ca32468..857ed8b067 100644 --- a/webapp/packages/core/codegen.yml +++ b/webapp/packages/core/codegen.yml @@ -5,21 +5,28 @@ schema: - ../../../server/bundles/io.cloudbeaver.server/schema/service.sql.graphqls - ../../../server/bundles/io.cloudbeaver.service.metadata/schema/service.metadata.graphqls - ../../../server/bundles/io.cloudbeaver.service.data.transfer/schema/service.data.transfer.graphqls + - ../../../server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls documents: - ./src/**/*.gql generates: src/sdk/sdk.ts: plugins: - - add: '/* eslint-disable */' + - add: '/* eslint-disable max-len */' - typescript - typescript-operations - typescript-graphql-request config: skipTypename: true + documentMode : 'string' + avoidOptionals: + field: false + inputValue: false + object: false maybeValue: T scalars: Object: any + JSON: "{ [key: string]: any }" DateTime: any hooks: afterAllFileWrite: - - prettier --write --config ../../.prettierrc.js + - eslint --fix diff --git a/webapp/packages/core/configs/webpack/common-exp.js b/webapp/packages/core/configs/webpack/common-exp.js index c8ef080434..65f87f11e8 100644 --- a/webapp/packages/core/configs/webpack/common-exp.js +++ b/webapp/packages/core/configs/webpack/common-exp.js @@ -4,14 +4,6 @@ const path = require('path'); const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); module.exports = (env, argv) => merge(commonConfig(env, argv), { - resolve: { - alias: { - - // rewrite imports for unnecessary work in `web/src/libs/sdk.ts` - 'graphql-tag': path.join(__dirname, '../../fix-gql.js'), - graphql: path.join(__dirname, '../../fix-gql.js'), - }, - }, context: path.resolve(__dirname, '../../src'), plugins: [ new ForkTsCheckerWebpackPlugin({ diff --git a/webapp/packages/core/fix-gql.js b/webapp/packages/core/fix-gql.js deleted file mode 100644 index 5f2d0259de..0000000000 --- a/webapp/packages/core/fix-gql.js +++ /dev/null @@ -1,5 +0,0 @@ -/** - * rewrite imports for unnecessary work in `webapp/packages/core/src/libs/sdk.ts` - */ -module.exports = strings => strings[0] -module.exports.print = v => v diff --git a/webapp/packages/core/package.json b/webapp/packages/core/package.json index 7466cb744f..1d0b096cdc 100644 --- a/webapp/packages/core/package.json +++ b/webapp/packages/core/package.json @@ -35,8 +35,6 @@ }, "peerDependencies": { "go-split": "^1.3.2", - "graphql": "^14.5.8", - "graphql-tag": "^2.10.1", "mobx": "^5.15.4", "mobx-react": "^6.2.2", "react": "^16.13.1", diff --git a/webapp/packages/core/src/AppBootstrap.ts b/webapp/packages/core/src/AppBootstrap.ts index f2f96ce8a3..c9c14e5024 100644 --- a/webapp/packages/core/src/AppBootstrap.ts +++ b/webapp/packages/core/src/AppBootstrap.ts @@ -8,8 +8,6 @@ import { ConnectionDialogsService, - ConnectionsManagerService, - NodesManagerService, NavigationTabsService, NavigationTreeContextMenuService, LogViewerMenuService, ConnectionSchemaManagerService, } from '@dbeaver/core/app'; import { injectable } from '@dbeaver/core/di'; @@ -19,6 +17,9 @@ import { LocalizationService } from '@dbeaver/core/localization'; import { PermissionsService } 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. @@ -28,19 +29,17 @@ import { ThemeService } from '@dbeaver/core/theming'; @injectable() export class AppBootstrap { - constructor(private exceptionsCatcherService: ExceptionsCatcherService, - 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, - private connectionSchemaManagerService: ConnectionSchemaManagerService, - private permissionsService: PermissionsService) { - } + constructor( + private exceptionsCatcherService: ExceptionsCatcherService, + private localizationService: LocalizationService, + private themeService: ThemeService, + private connectionDialogService: ConnectionDialogsService, + private logViewerMenuService: LogViewerMenuService, + private sessionExpireService: SessionExpireService, + private navigationTreeContextMenuService: NavigationTreeContextMenuService, + private navigationTabsService: NavigationTabsService, + private connectionSchemaManagerService: ConnectionSchemaManagerService, + ) { } async init() { this.exceptionsCatcherService.subscribe(); @@ -48,16 +47,11 @@ export class AppBootstrap { await this.localizationService.init(); await this.themeService.init(); - await this.permissionsService.update(); this.connectionSchemaManagerService.registerCallbacks(); this.navigationTreeContextMenuService.registerMenuItems(); this.connectionDialogService.registerMenuItems(); this.logViewerMenuService.registerMenuItems(); - - await this.connectionsManager.restoreConnections(); - - await this.nodesManagerService.updateRootChildren(); } async doAfterPluginsInit() { diff --git a/webapp/packages/core/src/app/NavigationTree/NavigationTree.tsx b/webapp/packages/core/src/app/NavigationTree/NavigationTree.tsx index 988739c26d..b7ec6f1113 100644 --- a/webapp/packages/core/src/app/NavigationTree/NavigationTree.tsx +++ b/webapp/packages/core/src/app/NavigationTree/NavigationTree.tsx @@ -9,6 +9,8 @@ import { observer } from 'mobx-react'; import styled, { css } from 'reshadow'; +import { usePermission, EPermission } from '@dbeaver/core/root'; + import { useChildren } from '../shared/useChildren'; import { NavigationTreeNode } from './NavigationTreeNode/NavigationTreeNode'; @@ -38,6 +40,11 @@ const navigationTreeStyles = css` export const NavigationTree = observer(function NavigationTree() { const nodeChildren = useChildren(); + const isEnabled = usePermission(EPermission.public); + + if (!isEnabled) { + return null; + } if (!nodeChildren || nodeChildren.children.length === 0) { return styled(navigationTreeStyles)( diff --git a/webapp/packages/core/src/app/Notifications/NotificationsItem/ErrorDetailsDialog/styles.ts b/webapp/packages/core/src/app/Notifications/NotificationsItem/ErrorDetailsDialog/styles.ts index 5ecac4a409..3e2eb2616e 100644 --- a/webapp/packages/core/src/app/Notifications/NotificationsItem/ErrorDetailsDialog/styles.ts +++ b/webapp/packages/core/src/app/Notifications/NotificationsItem/ErrorDetailsDialog/styles.ts @@ -15,8 +15,10 @@ export const styles = css` } controls { display: flex; + flex: 1; height: 100%; align-items: center; + margin: auto; flex-direction: row-reverse; } Button { diff --git a/webapp/packages/core/src/app/TopNavBar/Fill.tsx b/webapp/packages/core/src/app/TopNavBar/Fill.tsx new file mode 100644 index 0000000000..54bdf739be --- /dev/null +++ b/webapp/packages/core/src/app/TopNavBar/Fill.tsx @@ -0,0 +1,19 @@ +/* + * 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 styled, { css } from 'reshadow'; + +const styles = css` + fill { + flex: 1; + } +`; + +export function Fill() { + return styled(styles)(); +} diff --git a/webapp/packages/core/src/app/TopNavBar/Logo.tsx b/webapp/packages/core/src/app/TopNavBar/Logo.tsx new file mode 100644 index 0000000000..4439d512e3 --- /dev/null +++ b/webapp/packages/core/src/app/TopNavBar/Logo.tsx @@ -0,0 +1,43 @@ +/* + * 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 { observer } from 'mobx-react'; +import styled, { css } from 'reshadow'; + +import { Icon } from '@dbeaver/core/blocks'; +import { useService } from '@dbeaver/core/di'; +import { ServerService } from '@dbeaver/core/root'; + +declare const version: string; // declared in webpack DefinePlugin // todo move to enviroment? + +const styles = css` + logo { + height: 100%; + display: flex; + align-items: center; + margin-right: 16px; + width: 250px; + } + + Icon { + height: 24px; + width: auto; + margin-bottom: 2px; + } +`; + +export const Logo = observer(function Logo() { + const serverService = useService(ServerService); + const title = `Frontend: ${version}\nBackend: ${serverService.config.data?.version}`; + + return styled(styles)( + + + + ); +}); diff --git a/webapp/packages/core/src/app/TopNavBar/SettingsMenu/settingsMenuStyles.ts b/webapp/packages/core/src/app/TopNavBar/SettingsMenu/settingsMenuStyles.ts index 1bc41b2d7a..cd8d29b2c4 100644 --- a/webapp/packages/core/src/app/TopNavBar/SettingsMenu/settingsMenuStyles.ts +++ b/webapp/packages/core/src/app/TopNavBar/SettingsMenu/settingsMenuStyles.ts @@ -25,7 +25,6 @@ export const settingsMenuStyles = css` } menu-panel-item { flex-direction: row-reverse; - & menu-item-text { text-align: right; } diff --git a/webapp/packages/core/src/app/TopNavBar/TopNavBar.tsx b/webapp/packages/core/src/app/TopNavBar/TopNavBar.tsx index 7a31466a7d..f1ac6baa5f 100644 --- a/webapp/packages/core/src/app/TopNavBar/TopNavBar.tsx +++ b/webapp/packages/core/src/app/TopNavBar/TopNavBar.tsx @@ -6,34 +6,21 @@ * you may not use this file except in compliance with the License. */ -import { observer } from 'mobx-react'; import styled from 'reshadow'; -import { Icon } from '@dbeaver/core/blocks'; +import { Placeholder } from '@dbeaver/core/blocks'; import { useService } from '@dbeaver/core/di'; -import { ServerService } from '@dbeaver/core/root'; import { useStyles } from '@dbeaver/core/theming'; -import { ConnectionSelector } from './ConnectionSchemaManager/ConnectionSelector/ConnectionSelector'; -import { MainMenu } from './MainMenu/MainMenu'; -import { SettingsMenu } from './SettingsMenu/SettingsMenu'; +import { TopNavService } from './TopNavBarService'; import { topNavBarStyles } from './topNavBarStyles'; - -declare const version: string; // declared in webpack DefinePlugin // todo move to enviroment? - -export const TopNavBar = observer(function TopNavBar() { - const serverService = useService(ServerService); - const title = `Frontend: ${version}\nBackend: ${serverService.config.data?.version}`; +export function TopNavBar() { + const topNavBarService = useService(TopNavService); return styled(useStyles(topNavBarStyles))(
- - - - - - +
); -}); +} diff --git a/webapp/packages/core/src/app/TopNavBar/TopNavBarService.ts b/webapp/packages/core/src/app/TopNavBar/TopNavBarService.ts new file mode 100644 index 0000000000..bdedd3ced4 --- /dev/null +++ b/webapp/packages/core/src/app/TopNavBar/TopNavBarService.ts @@ -0,0 +1,29 @@ +/* + * 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 { PlaceholderContainer } from '@dbeaver/core/blocks'; +import { injectable } from '@dbeaver/core/di'; + +import { ConnectionSelector } from './ConnectionSchemaManager/ConnectionSelector/ConnectionSelector'; +import { Fill } from './Fill'; +import { Logo } from './Logo'; +import { MainMenu } from './MainMenu/MainMenu'; +import { SettingsMenu } from './SettingsMenu/SettingsMenu'; + +@injectable() +export class TopNavService { + readonly placeholder = new PlaceholderContainer(); + + constructor() { + this.placeholder.add(Logo, 0); + this.placeholder.add(MainMenu, 1); + this.placeholder.add(ConnectionSelector, 2); + this.placeholder.add(Fill, 3); + this.placeholder.add(SettingsMenu, 4); + } +} diff --git a/webapp/packages/core/src/app/TopNavBar/topNavBarStyles.ts b/webapp/packages/core/src/app/TopNavBar/topNavBarStyles.ts index 28c201a00a..18aa0f60e8 100644 --- a/webapp/packages/core/src/app/TopNavBar/topNavBarStyles.ts +++ b/webapp/packages/core/src/app/TopNavBar/topNavBarStyles.ts @@ -25,18 +25,5 @@ export const topNavBarStyles = composes( padding: 0 16px; z-index: 1; } - logo { - height: 100%; - display: flex; - align-items: center; - margin-right: 16px; - width: 250px; - - & Icon { - height: 24px; - width: auto; - margin-bottom: 2px; - } - } ` ); diff --git a/webapp/packages/core/src/app/index.ts b/webapp/packages/core/src/app/index.ts index bd814a217e..13060bf6cf 100644 --- a/webapp/packages/core/src/app/index.ts +++ b/webapp/packages/core/src/app/index.ts @@ -1,7 +1,8 @@ // Services export * from './TopNavBar/MainMenu/MainMenuService'; -export * from './TopNavBar/ConnectionSchemaManager/ConnectionSchemaManagerService'; export * from './TopNavBar/SettingsMenu/SettingsMenuService'; +export * from './TopNavBar/ConnectionSchemaManager/ConnectionSchemaManagerService'; +export * from './TopNavBar/TopNavBarService'; export * from './shared/ConnectionsManager/ConnectionsManagerService'; export * from './shared/ConnectionsManager/ConnectionDialogsService'; export * from './shared/ConnectionsManager/extensions/IConnectionProvider'; diff --git a/webapp/packages/core/src/app/shared/ConnectionsManager/ConnectionsManagerService.ts b/webapp/packages/core/src/app/shared/ConnectionsManager/ConnectionsManagerService.ts index 7884822bf1..41b55991ed 100644 --- a/webapp/packages/core/src/app/shared/ConnectionsManager/ConnectionsManagerService.ts +++ b/webapp/packages/core/src/app/shared/ConnectionsManager/ConnectionsManagerService.ts @@ -65,9 +65,12 @@ export class ConnectionsManagerService { onOpenConnection = new Subject(); onCloseConnection = new Subject(); - constructor(private graphQLService: GraphQLService, - private nodesManagerService: NodesManagerService, - private sessionService: SessionService) { + constructor( + private graphQLService: GraphQLService, + private nodesManagerService: NodesManagerService, + private sessionService: SessionService + ) { + this.sessionService.onUpdate.subscribe(this.restoreConnections.bind(this)); } getDBDrivers(): Map { @@ -114,9 +117,8 @@ export class ConnectionsManagerService { } async closeConnectionAsync(id: string, skipNodesRefresh?: boolean): Promise { - await this.nodesManagerService.closeConnection(id); - this.onCloseConnection.next(id); await this.graphQLService.gql.closeConnection({ id }); + await this.afterConnectionClose(id); this.connectionsMap.delete(id); if (!skipNodesRefresh) { @@ -129,16 +131,29 @@ export class ConnectionsManagerService { return data.get(connectionId)!; } - async restoreConnections() { + private async afterConnectionClose(id: string) { + await this.nodesManagerService.closeConnection(id); + this.onCloseConnection.next(id); + } + + private async restoreConnections() { const config = await this.sessionService.session.load(); if (!config) { return; } + let connectionsToRemove = this.connections.concat(); // TODO: connections must be string[] for (const connection of config.connections) { await this.restoreConnection(connection); + connectionsToRemove = connectionsToRemove.filter(({ id }) => id !== connection.id); } + + for (const connection of connectionsToRemove) { + await this.afterConnectionClose(connection.id); + this.connectionsMap.delete(connection.id); + } + await this.nodesManagerService.updateRootChildren(); } @@ -187,6 +202,9 @@ export class ConnectionsManagerService { // TODO: Must be loaded based on connection id // const { connection } = await this.graphQLService.gql.connectionState({ id }); + if (this.connectionsMap.has(connection.id)) { + return; + } this.connectionsMap.set(connection.id, connection); this.onOpenConnection.next(connection); } diff --git a/webapp/packages/core/src/app/shared/InlineEditor/EditorDialog.tsx b/webapp/packages/core/src/app/shared/InlineEditor/EditorDialog.tsx index 55d40b3f11..7e88f60b27 100644 --- a/webapp/packages/core/src/app/shared/InlineEditor/EditorDialog.tsx +++ b/webapp/packages/core/src/app/shared/InlineEditor/EditorDialog.tsx @@ -20,8 +20,10 @@ import { useStyles } from '@dbeaver/core/theming'; const styles = css` controls { display: flex; + flex: 1; height: 100%; align-items: center; + margin: auto; flex-direction: row-reverse; } Button { diff --git a/webapp/packages/core/src/app/shared/NodesManager/NodesManagerService.ts b/webapp/packages/core/src/app/shared/NodesManager/NodesManagerService.ts index fee1035547..df9372b677 100644 --- a/webapp/packages/core/src/app/shared/NodesManager/NodesManagerService.ts +++ b/webapp/packages/core/src/app/shared/NodesManager/NodesManagerService.ts @@ -7,6 +7,7 @@ */ import { injectable } from '@dbeaver/core/di'; +import { PermissionsService, EPermission } from '@dbeaver/core/root'; import { DatabaseObjectInfo, GraphQLService } from '@dbeaver/core/sdk'; import { INavigator } from '../Navigation/INavigator'; @@ -51,7 +52,8 @@ export class NodesManagerService { private nodesStore = new NodesStore(); constructor(private graphQLService: GraphQLService, - private navigationService: NavigationService) { + private navigationService: NavigationService, + private permissionsService: PermissionsService) { this.navigator = this.navigationService.createNavigator( data => data.nodeId, @@ -134,7 +136,10 @@ export class NodesManagerService { } async updateRootChildren() { - return this.updateChildren(ROOT_NODE_PATH); + if (!await this.permissionsService.hasAsync(EPermission.public)) { + return; + } + this.updateChildren(ROOT_NODE_PATH); } async updateChildrenDatabaseObjectInfo(nodeId: string) { diff --git a/webapp/packages/core/src/app/shared/ToolsPanel/LogViewTab/LogViewerService.ts b/webapp/packages/core/src/app/shared/ToolsPanel/LogViewTab/LogViewerService.ts index e41ac074b4..3003fa693b 100644 --- a/webapp/packages/core/src/app/shared/ToolsPanel/LogViewTab/LogViewerService.ts +++ b/webapp/packages/core/src/app/shared/ToolsPanel/LogViewTab/LogViewerService.ts @@ -10,6 +10,7 @@ import { action, observable } from 'mobx'; import { injectable } from '@dbeaver/core/di'; import { NotificationService } from '@dbeaver/core/eventsLog'; +import { PermissionsService, EPermission } from '@dbeaver/core/root'; import { GraphQLService } from '@dbeaver/core/sdk'; import { CoreSettingsService } from '@dbeaver/core/settings'; import { uuid } from '@dbeaver/core/utils'; @@ -19,23 +20,29 @@ import { ILogEntry } from './ILogEntry'; @injectable() export class LogViewerService { - @observable isActive = false; + @observable _isActive = false; + + get isActive() { + return this._isActive; + } @observable private log: ILogEntry[] = []; private interval: any = null; - constructor(private graphQLService: GraphQLService, - private coreSettingsService: CoreSettingsService, - private notificationService: NotificationService) { + constructor( + private graphQLService: GraphQLService, + private coreSettingsService: CoreSettingsService, + private notificationService: NotificationService, + private permissionsService: PermissionsService, + ) { + this.permissionsService.onUpdate.subscribe(this.stopIfHasNoPermission.bind(this)); } toggle() { - if (this.isActive) { + if (this._isActive) { this.stopLog(); - this.isActive = false; } else { this.startLog(); - this.isActive = true; } } @@ -44,9 +51,13 @@ export class LogViewerService { } async startLog() { - if (this.interval) { + if (this._isActive) { return; } + if (!this.isLogViewerAvailable()) { + throw new Error('Access denied'); + } + this._isActive = true; await this.updateLog(); const refreshInterval = this.coreSettingsService.settings.getValue('app.logViewer.refreshInterval'); this.interval = setInterval(() => { @@ -59,6 +70,7 @@ export class LogViewerService { clearInterval(this.interval); this.interval = null; } + this._isActive = false; } @action @@ -66,24 +78,6 @@ export class LogViewerService { this.log = []; } - private async updateLog() { - try { - const newEntries = await this.loadLog(); - this.addNewEntries(newEntries); - } catch (e) { - this.notificationService.logException(e, 'Failed to load log'); - } - } - - @action - addNewEntries(entries: ILogEntry[]) { - this.log.unshift(...entries.reverse()); - 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 { const maxLogEntries = this.coreSettingsService.settings.getValue('app.logViewer.maxLogEntries'); const { log } = await this.graphQLService.gql.readSessionLog({ @@ -96,4 +90,36 @@ export class LogViewerService { })); return entries; } + + @action + private addNewEntries(entries: ILogEntry[]) { + this.log.unshift(...entries.reverse()); + const maxLogEntries = this.coreSettingsService.settings.getValue('app.logViewer.maxLogEntries'); + if (this.log.length > maxLogEntries) { + this.log.splice(maxLogEntries, this.log.length - maxLogEntries); + } + } + + private async updateLog() { + if (!this.isLogViewerAvailable()) { + return; + } + + try { + const newEntries = await this.loadLog(); + this.addNewEntries(newEntries); + } catch (e) { + this.notificationService.logException(e, 'Failed to load log'); + } + } + + private stopIfHasNoPermission() { + if (this._isActive && !this.isLogViewerAvailable()) { + this.stopLog(); + } + } + + private isLogViewerAvailable() { + return this.permissionsService.has(EPermission.public); + } } diff --git a/webapp/packages/core/src/blocks/Placeholder/Placeholder.tsx b/webapp/packages/core/src/blocks/Placeholder/Placeholder.tsx new file mode 100644 index 0000000000..5f3a8ddb26 --- /dev/null +++ b/webapp/packages/core/src/blocks/Placeholder/Placeholder.tsx @@ -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 { observer } from 'mobx-react'; + +import { PlaceholderContainer } from './PlaceholderContainer'; + +type Props = T extends unknown ? { + container: PlaceholderContainer; + context?: T; +} : { + container: PlaceholderContainer; + context: T; +} + +export const Placeholder = observer(function Placeholder({ container, context }: Props) { + return ( + <> + {container.get().map(({ id, component: Component }) => )} + + ); +}); diff --git a/webapp/packages/core/src/blocks/Placeholder/PlaceholderContainer.ts b/webapp/packages/core/src/blocks/Placeholder/PlaceholderContainer.ts new file mode 100644 index 0000000000..52c589a657 --- /dev/null +++ b/webapp/packages/core/src/blocks/Placeholder/PlaceholderContainer.ts @@ -0,0 +1,57 @@ +/* + * 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 { observable } from 'mobx'; + +import { uuid } from '@dbeaver/core/utils'; + +export type PlaceholderProps = { + context: T; +} + +export type PlaceholderComponent = React.FunctionComponent> + +export type PlaceholderElement = { + id: string; + component: PlaceholderComponent; + order?: number; +} + +export class PlaceholderContainer { + private placeholders = observable>([], { deep: false }); + + get() { + return this.placeholders; + } + + add(component: PlaceholderComponent, order?: number) { + const placeholder: PlaceholderElement = { + id: uuid(), + component, + order, + }; + + if (!order) { + this.placeholders.push(placeholder); + return; + } + + this.placeholders.splice(this.findPosition(order), 0, placeholder); + } + + private findPosition(order: number) { + const position = this.placeholders + .findIndex(placeholder => placeholder.order === undefined || order <= placeholder.order); + + if (position === -1) { + return this.placeholders.length; + } + + return position; + } +} diff --git a/webapp/packages/core/src/blocks/PropertiesTable/IProperty.ts b/webapp/packages/core/src/blocks/PropertiesTable/IProperty.ts index 3323e610ea..79076fdc00 100644 --- a/webapp/packages/core/src/blocks/PropertiesTable/IProperty.ts +++ b/webapp/packages/core/src/blocks/PropertiesTable/IProperty.ts @@ -8,7 +8,8 @@ export interface IProperty { id: string; - name: string; + key: string; + displayName?: string; defaultValue?: string; description?: string; validValues?: string[]; diff --git a/webapp/packages/core/src/blocks/PropertiesTable/PropertiesTable.tsx b/webapp/packages/core/src/blocks/PropertiesTable/PropertiesTable.tsx index 6bdd784005..ebb50709ae 100644 --- a/webapp/packages/core/src/blocks/PropertiesTable/PropertiesTable.tsx +++ b/webapp/packages/core/src/blocks/PropertiesTable/PropertiesTable.tsx @@ -26,7 +26,7 @@ type PropertiesState = { type PropertiesTableProps = { properties: IProperty[]; propertiesState?: PropertiesState; - onNameChange?(id: string, name: string): void; + onKeyChange?(id: string, name: string): void; onChange?(state: PropertiesState): void; onAdd?(): void; onRemove?(id: string): void; @@ -36,7 +36,7 @@ type PropertiesTableProps = { export const PropertiesTable = observer(function PropertiesTable({ propertiesState, properties, - onNameChange, + onKeyChange, onChange, onAdd, onRemove, @@ -44,22 +44,22 @@ export const PropertiesTable = observer(function PropertiesTable({ }: PropertiesTableProps) { const translate = useTranslate(); const state = useLocalStore(() => (propertiesState || {})); - const changeName = useCallback((id: string, name: string) => { + const changeName = useCallback((id: string, key: string) => { const property = properties.find(property => property.id === id); if (!property) { return; } - if (state[property.name] !== undefined) { - state[name] = state[property.name]; - delete state[property.name]; + if (state[property.key] !== undefined) { + state[key] = state[property.key]; + delete state[property.key]; } - if (onNameChange) { - onNameChange(property.name, name); + if (onKeyChange) { + onKeyChange(property.key, key); } - property.name = name; + property.key = key; }, [properties]); const changeValue = useCallback((id: string, value: string) => { @@ -69,7 +69,7 @@ export const PropertiesTable = observer(function PropertiesTable({ return; } - state[property.name] = value; + state[property.key] = value; if (onChange) { onChange(state); @@ -83,8 +83,8 @@ export const PropertiesTable = observer(function PropertiesTable({ return; } - if (state[property.name] !== undefined) { - delete state[property.name]; + if (state[property.key] !== undefined) { + delete state[property.key]; } if (onRemove) { @@ -93,8 +93,8 @@ export const PropertiesTable = observer(function PropertiesTable({ properties.splice(properties.indexOf(property), 1); }, [properties, onRemove]); - const isNameUnique = useCallback( - (name: string) => properties.filter(property => property.name === name).length === 1, + const isKeyUnique = useCallback( + (key: string) => properties.filter(property => property.key === key).length === 1, [] ); @@ -116,11 +116,11 @@ export const PropertiesTable = observer(function PropertiesTable({ ))} diff --git a/webapp/packages/core/src/blocks/PropertiesTable/PropertyItem.tsx b/webapp/packages/core/src/blocks/PropertiesTable/PropertyItem.tsx index 9b317dc4ed..a5aa447da4 100644 --- a/webapp/packages/core/src/blocks/PropertiesTable/PropertyItem.tsx +++ b/webapp/packages/core/src/blocks/PropertiesTable/PropertyItem.tsx @@ -80,7 +80,7 @@ const styles = composes( &[|edited] { font-weight: 600; } - &:not(:focus):not([|focus]) { + &:global([readonly]), &:not(:focus):not([|focus]) { background: transparent; border: solid 1px transparent; } @@ -105,7 +105,7 @@ const styles = composes( ` ); -type PropertyItemProps = { +type Props = { property: IProperty; value?: string; onNameChange(staticId: string, newId: string): void; @@ -121,12 +121,12 @@ export const PropertyItem = observer(function PropertyItem({ onValueChange, onRemove, error, -}: PropertyItemProps) { - const isEditable = property.name !== property.id; +}: Props) { + const isKeyEditable = !property.displayName; const edited = value !== undefined && value !== property.defaultValue; const [focus, setFocus] = useState(false); - const nameInputRef = useRef(null); - const handleNameChange = useCallback((value: string) => onNameChange(property.id, value), [property]); + const keyInputRef = useRef(null); + const handleKeyChange = useCallback((key: string) => onNameChange(property.id, key), [property]); const handleValueChange = useCallback( (value: string) => onValueChange(property.id, value), [property] @@ -134,8 +134,8 @@ export const PropertyItem = observer(function PropertyItem({ const handleRemove = useCallback(() => onRemove(property.id), [property]); useEffect(() => { - if (nameInputRef.current && isEditable) { - nameInputRef.current.focus(); + if (keyInputRef.current && isKeyEditable) { + keyInputRef.current.focus(); } }, []); @@ -145,12 +145,12 @@ export const PropertyItem = observer(function PropertyItem({ - {property.name || property.id} + {property.displayName || property.key} @@ -176,7 +176,7 @@ export const PropertyItem = observer(function PropertyItem({ )} - {isEditable && ( + {isKeyEditable && ( diff --git a/webapp/packages/core/src/blocks/Tabs/Tab/Tab.tsx b/webapp/packages/core/src/blocks/Tabs/Tab/Tab.tsx index e867c223eb..aa90c68c4e 100644 --- a/webapp/packages/core/src/blocks/Tabs/Tab/Tab.tsx +++ b/webapp/packages/core/src/blocks/Tabs/Tab/Tab.tsx @@ -18,7 +18,7 @@ import { TabsContext } from '../TabsContext'; type TabProps = PropsWithChildren<{ tabId: string; className?: string; - onOpen: (tabId: string) => void; + onOpen?: (tabId: string) => void; onClose?: (tabId: string) => void; }> @@ -36,8 +36,10 @@ export function Tab({ const handleOpen = useCallback((e: React.MouseEvent) => { e.preventDefault(); - onOpen(tabId); - }, []); + if (onOpen) { + onOpen(tabId); + } + }, [onOpen]); const handleClose = useCallback((e: React.MouseEvent) => { e.preventDefault(); @@ -54,12 +56,14 @@ export function Tab({ className={className} onClick={handleOpen} > - {children} - {onClose && ( - - - - )} + + {children} + {onClose && ( + + + + )} + ); } diff --git a/webapp/packages/core/src/blocks/Tabs/TabList.tsx b/webapp/packages/core/src/blocks/Tabs/TabList.tsx new file mode 100644 index 0000000000..7e4744743a --- /dev/null +++ b/webapp/packages/core/src/blocks/Tabs/TabList.tsx @@ -0,0 +1,22 @@ +/* + * 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 { useContext } from 'react'; +import { TabList as BaseTabList, TabListOptions } from 'reakit/Tab'; + +import { TabsContext } from './TabsContext'; + +export function TabList(props: React.PropsWithChildren) { + const state = useContext(TabsContext); + + if (!state) { + throw new Error('Tabs context was not provided'); + } + + return ; +} diff --git a/webapp/packages/core/src/blocks/Tabs/TabsBox/TabsBox.tsx b/webapp/packages/core/src/blocks/Tabs/TabsBox/TabsBox.tsx index b13f3b9265..98f43cb4f1 100644 --- a/webapp/packages/core/src/blocks/Tabs/TabsBox/TabsBox.tsx +++ b/webapp/packages/core/src/blocks/Tabs/TabsBox/TabsBox.tsx @@ -6,15 +6,12 @@ * you may not use this file except in compliance with the License. */ -import { - useMemo, PropsWithChildren, ReactNode, useEffect, -} from 'react'; -import { useTabState } from 'reakit/Tab'; +import { PropsWithChildren, ReactNode } from 'react'; import styled from 'reshadow'; import { useStyles, Style } from '@dbeaver/core/theming'; -import { TabsContext } from '../TabsContext'; +import { TabsState } from '../TabsState'; type TabsBoxProps = PropsWithChildren<{ currentTabId: string; @@ -30,16 +27,9 @@ export function TabsBox({ className, style = [], }: TabsBoxProps) { - const state = useTabState({ - selectedId: currentTabId, - manual: true, - }); - state.selectedId = currentTabId; - useEffect(() => state.select(currentTabId), [currentTabId]); // hack currentId and selectedId not works - const value = useMemo(() => state, Object.values(state)); return styled(useStyles(...style))( - + {tabs && ( @@ -50,6 +40,6 @@ export function TabsBox({ {children} - + ); } diff --git a/webapp/packages/core/src/blocks/Tabs/TabsState.tsx b/webapp/packages/core/src/blocks/Tabs/TabsState.tsx new file mode 100644 index 0000000000..b0ae6738dc --- /dev/null +++ b/webapp/packages/core/src/blocks/Tabs/TabsState.tsx @@ -0,0 +1,40 @@ +/* + * 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 { useEffect, useMemo } from 'react'; +import { useTabState } from 'reakit/Tab'; + +import { TabsContext } from './TabsContext'; + +type Props = React.PropsWithChildren<{ + selectedId?: string; + currentTabId?: string | null; +}> + +export function TabsState({ selectedId, currentTabId, children }: Props) { + const state = useTabState({ + selectedId: selectedId || currentTabId, + manual: true, + }); + if (currentTabId) { + state.selectedId = currentTabId; + } + useEffect(() => { + if (!currentTabId) { + return; + } + state.select(currentTabId); + }, [currentTabId]); // hack currentId and selectedId not works + const value = useMemo(() => state, Object.values(state)); + + return ( + + {children} + + ); +} diff --git a/webapp/packages/core/src/blocks/Tabs/index.ts b/webapp/packages/core/src/blocks/Tabs/index.ts index b0231505c1..683b15242a 100644 --- a/webapp/packages/core/src/blocks/Tabs/index.ts +++ b/webapp/packages/core/src/blocks/Tabs/index.ts @@ -1,6 +1,8 @@ export * from './Tab'; export * from './TabPanel'; export * from './TabsContext'; +export * from './TabsState'; +export * from './TabList'; export * from './ITab'; diff --git a/webapp/packages/core/src/blocks/index.ts b/webapp/packages/core/src/blocks/index.ts index c4d1f4138d..a688b5421a 100644 --- a/webapp/packages/core/src/blocks/index.ts +++ b/webapp/packages/core/src/blocks/index.ts @@ -18,3 +18,5 @@ export * from './SanitizedHTML/SanitizedHTML'; export * from './ItemList'; export * from './PropertiesTable'; export * from './IconButton'; +export * from './Placeholder/Placeholder'; +export * from './Placeholder/PlaceholderContainer'; diff --git a/webapp/packages/core/src/dialogs/CommonDialog/CommonDialogService.ts b/webapp/packages/core/src/dialogs/CommonDialog/CommonDialogService.ts index b107ac078e..724e0f2e3f 100644 --- a/webapp/packages/core/src/dialogs/CommonDialog/CommonDialogService.ts +++ b/webapp/packages/core/src/dialogs/CommonDialog/CommonDialogService.ts @@ -10,8 +10,13 @@ import { observable } from 'mobx'; import { injectable } from '@dbeaver/core/di'; +export type DialogOptions = { + persistent?: boolean; +} + export type DialogComponentProps = { payload: TPayload; + options?: DialogOptions; resolveDialog(result: TResult | null): void; rejectDialog(): void; // the dialog was closed by cancel button or backdrop click } @@ -23,6 +28,7 @@ export type DialogComponent = React.ElementType< export interface DialogInternal { component: DialogComponent; payload: any; + options?: DialogOptions; resolve(result: any): void; } @@ -32,14 +38,16 @@ export class CommonDialogService { // note that if dialog is closed by user it will be resolved with null async open( - dialog: DialogComponent, - payload: TPayload + component: DialogComponent, + payload: TPayload, + options?: DialogOptions, ): Promise { return new Promise((resolve, reject) => { const dialogInternal: DialogInternal = { - component: dialog, + component, payload, resolve, + options, }; this.dialogs.push(dialogInternal); }); diff --git a/webapp/packages/core/src/dialogs/CommonDialog/CommonDialogWrapper.tsx b/webapp/packages/core/src/dialogs/CommonDialog/CommonDialogWrapper.tsx index 3ca12500ea..59b526b071 100644 --- a/webapp/packages/core/src/dialogs/CommonDialog/CommonDialogWrapper.tsx +++ b/webapp/packages/core/src/dialogs/CommonDialog/CommonDialogWrapper.tsx @@ -13,7 +13,7 @@ import { composes, useStyles } from '@dbeaver/core/theming'; export type CommonDialogWrapperProps = { title: string; - onReject: () => any; + onReject?: () => void; className?: string; noBodyPadding?: boolean; footer?: JSX.Element | boolean; @@ -39,6 +39,7 @@ const style = composes( padding: 0; margin: 0; border: none; + height: auto; min-width: 748px; max-height: 100%; border-radius: 0.25rem; @@ -54,6 +55,8 @@ const style = composes( } dialog-body { box-sizing: border-box; + display: flex; + flex-direction: column; flex: 1; overflow: auto; padding: 18px 24px; @@ -80,6 +83,7 @@ const style = composes( } footer { composes: theme-elevation-z10 from global; + display: flex; z-index: 0; box-sizing: border-box; min-height: 72px; @@ -99,17 +103,19 @@ export function CommonDialogWrapper({ }: CommonDialogWrapperProps) { return styled(useStyles(style))( - +

{title}

- - - + {onReject && ( + + + + )}
{header}
- {children} + {children}
{footer}
diff --git a/webapp/packages/core/src/dialogs/CommonDialog/DialogsPortal.tsx b/webapp/packages/core/src/dialogs/CommonDialog/DialogsPortal.tsx index adfaa1b8e8..15e77e73be 100644 --- a/webapp/packages/core/src/dialogs/CommonDialog/DialogsPortal.tsx +++ b/webapp/packages/core/src/dialogs/CommonDialog/DialogsPortal.tsx @@ -13,6 +13,7 @@ import { DialogBackdrop, useDialogState, } from 'reakit/Dialog'; +import { Portal } from 'reakit/Portal'; import styled from 'reshadow'; import { useService } from '@dbeaver/core/di'; @@ -63,18 +64,26 @@ function NestedDialog(props: NestedDialogType) { (result: any) => props.resolveDialog(props.dialog, result), [props.dialog, props.resolveDialog] ); + const backdropClickCallback = useCallback(() => { + if (!props.dialog.options?.persistent) { + handleReject(); + } + }, [props.dialog.options?.persistent, handleReject]); const DialogComponent = props.dialog.component; // TODO: place Dialog inside CommonDialogWrapper, so we can pass aria-label return styled(styles)( - - - - + <> + + + + + ); } diff --git a/webapp/packages/core/src/dialogs/CommonDialog/styles.ts b/webapp/packages/core/src/dialogs/CommonDialog/styles.ts index 8b5d9a6f79..5a48c6f4a6 100644 --- a/webapp/packages/core/src/dialogs/CommonDialog/styles.ts +++ b/webapp/packages/core/src/dialogs/CommonDialog/styles.ts @@ -11,24 +11,23 @@ import { css } from 'reshadow'; export const dialogStyles = css` DialogBackdrop { box-sizing: border-box; - background-color: #0000007a; + background-color: rgba(0,0,0,0.48); position: fixed; top: 0px; right: 0px; bottom: 0px; left: 0px; + z-index: 999; } Dialog { box-sizing: border-box; display: flex; position: fixed; - align-items: center; - justify-content: center; - top: 0; - padding: 54px; - width: 100%; - height: 100%; + top: 50%; + left: 50%; + max-height: calc(100vh); + transform: translate(-50%, -50%); z-index: 999; } `; diff --git a/webapp/packages/core/src/dialogs/SessionExpire/SessionExpiredDialog.tsx b/webapp/packages/core/src/dialogs/SessionExpire/SessionExpiredDialog.tsx index 60a54e4643..66176949c1 100644 --- a/webapp/packages/core/src/dialogs/SessionExpire/SessionExpiredDialog.tsx +++ b/webapp/packages/core/src/dialogs/SessionExpire/SessionExpiredDialog.tsx @@ -26,9 +26,11 @@ const styles = css` } controls { display: flex; - justify-content: flex-end; + flex: 1; height: 100%; align-items: center; + margin: auto; + justify-content: flex-end; } `; diff --git a/webapp/packages/core/src/manifest.ts b/webapp/packages/core/src/manifest.ts index dd967ba44e..761a3cd237 100644 --- a/webapp/packages/core/src/manifest.ts +++ b/webapp/packages/core/src/manifest.ts @@ -11,7 +11,7 @@ import { MainMenuService, NavigationService, NavigationTreeService, NodesManagerService, ConnectionSchemaManagerService, NavigationTabsService, NavigationTreeContextMenuService, - SettingsMenuService, LogViewerService, LogViewerMenuService, + SettingsMenuService, LogViewerService, LogViewerMenuService, TopNavService, } from '@dbeaver/core/app'; import { PluginManifest } from '@dbeaver/core/di'; import { CommonDialogService, ContextMenuService, SessionExpireService } from '@dbeaver/core/dialogs'; @@ -55,6 +55,7 @@ export const coreManifest: PluginManifest = { LogViewerMenuService, LogViewerService, MainMenuService, + TopNavService, NavigationService, NavigationTabsService, NavigationTreeContextMenuService, diff --git a/webapp/packages/core/src/queries/Authentication/authLogin.gql b/webapp/packages/core/src/queries/Authentication/authLogin.gql new file mode 100644 index 0000000000..58d62196da --- /dev/null +++ b/webapp/packages/core/src/queries/Authentication/authLogin.gql @@ -0,0 +1,12 @@ +query authLogin( + $provider: ID! + $credentials: Object! +) { + user: authLogin(provider: $provider, credentials: $credentials) { + userId + displayName + authProvider + loginTime + message + } +} \ No newline at end of file diff --git a/webapp/packages/core/src/queries/Authentication/authLogout.gql b/webapp/packages/core/src/queries/Authentication/authLogout.gql new file mode 100644 index 0000000000..842069abfa --- /dev/null +++ b/webapp/packages/core/src/queries/Authentication/authLogout.gql @@ -0,0 +1,3 @@ +query authLogout { + authLogout +} \ No newline at end of file diff --git a/webapp/packages/core/src/queries/Authentication/getAuthProviders.gql b/webapp/packages/core/src/queries/Authentication/getAuthProviders.gql new file mode 100644 index 0000000000..ae72d185b2 --- /dev/null +++ b/webapp/packages/core/src/queries/Authentication/getAuthProviders.gql @@ -0,0 +1,31 @@ +query getAuthProviders { + providers: authProviders { + id + label + icon + description + defaultProvider + + #configurationParameters { + # id + # displayName + # description + # category + # dataType + # value + # validValues + # defaultValue + # features + #} + + credentialParameters { + id + displayName + description + admin + user + possibleValues + encryption + } + } +} \ No newline at end of file diff --git a/webapp/packages/core/src/queries/Authentication/getSessionUser.gql b/webapp/packages/core/src/queries/Authentication/getSessionUser.gql new file mode 100644 index 0000000000..ee457061b2 --- /dev/null +++ b/webapp/packages/core/src/queries/Authentication/getSessionUser.gql @@ -0,0 +1,9 @@ +query getSessionUser { + user: sessionUser { + userId + displayName + authProvider + loginTime + message + } +} \ No newline at end of file diff --git a/webapp/packages/core/src/root/PermissionsService.ts b/webapp/packages/core/src/root/PermissionsService.ts index 918f4dfd10..2500e2dbe1 100644 --- a/webapp/packages/core/src/root/PermissionsService.ts +++ b/webapp/packages/core/src/root/PermissionsService.ts @@ -6,31 +6,54 @@ * you may not use this file except in compliance with the License. */ +import { Subject, Observable } from 'rxjs'; + import { injectable } from '@dbeaver/core/di'; import { GraphQLService, CachedResource } from '@dbeaver/core/sdk'; +import { SessionService } from './SessionService'; + type PermissionsMetadata = { loaded: boolean; } +export enum EPermission { + public = 'public' +} + @injectable() export class PermissionsService { + readonly onUpdate: Observable; + + private updateSubject: Subject; private permissions = new CachedResource( new Map(), this.refreshAsync.bind(this), (_, { loaded }) => loaded ); + constructor( private graphQLService: GraphQLService, - ) { } + private sessionService: SessionService, + ) { + this.updateSubject = new Subject(); + this.onUpdate = this.updateSubject.asObservable(); + this.sessionService.onUpdate.subscribe(this.update.bind(this)); + } has(id: string): boolean { return this.permissions.data.has(id); } + async hasAsync(id: string): Promise { + const permissions = await this.permissions.load(); + return permissions.has(id); + } + async update() { await this.permissions.refresh(); + this.updateSubject.next(); } private async refreshAsync( diff --git a/webapp/packages/core/src/root/ServerService.ts b/webapp/packages/core/src/root/ServerService.ts index fb3a028e03..d0b209da30 100644 --- a/webapp/packages/core/src/root/ServerService.ts +++ b/webapp/packages/core/src/root/ServerService.ts @@ -33,8 +33,7 @@ export class ServerService { this.settings.clear(); parseJSONFlat( serverConfig.productConfiguration, - this.settings.setSelfValue.bind(this.settings), - undefined + this.settings.setSelfValue.bind(this.settings) ); } return serverConfig; diff --git a/webapp/packages/core/src/root/SessionService.ts b/webapp/packages/core/src/root/SessionService.ts index a6de0e62f6..2ebc87b235 100644 --- a/webapp/packages/core/src/root/SessionService.ts +++ b/webapp/packages/core/src/root/SessionService.ts @@ -6,6 +6,8 @@ * you may not use this file except in compliance with the License. */ +import { Subject, Observable } from 'rxjs'; + import { injectable } from '@dbeaver/core/di'; import { ConnectionInfo, GraphQLService, SessionInfo, CachedResource, @@ -21,12 +23,23 @@ export type SessionState = Pick !!data); readonly settings = new SessionSettingsService('session_settings'); + readonly onUpdate: Observable; - constructor(private graphQLService: GraphQLService) {} + private updateSubject: Subject; + + constructor(private graphQLService: GraphQLService) { + this.updateSubject = new Subject(); + this.onUpdate = this.updateSubject.asObservable(); + } + + async update() { + await this.session.refresh(); + } private async refreshSessionStateAsync(data: SessionState | undefined): Promise { const { session } = await this.graphQLService.gql.openSession(); + this.updateSubject.next(); return session; } } diff --git a/webapp/packages/core/src/root/index.ts b/webapp/packages/core/src/root/index.ts index 7343595740..61de6bf7e4 100644 --- a/webapp/packages/core/src/root/index.ts +++ b/webapp/packages/core/src/root/index.ts @@ -3,3 +3,4 @@ export * from './ServerService'; export * from './PermissionsService'; export * from '../settings/ServerSettingsService'; export * from '../settings/SessionSettingsService'; +export * from './usePermission'; diff --git a/webapp/packages/core/src/root/queries/serverConfig.gql b/webapp/packages/core/src/root/queries/serverConfig.gql index 44ef426a82..c67adc5aa2 100644 --- a/webapp/packages/core/src/root/queries/serverConfig.gql +++ b/webapp/packages/core/src/root/queries/serverConfig.gql @@ -8,6 +8,8 @@ query serverConfig { supportsCustomConnections supportsConnectionBrowser supportsWorkspaces + anonymousAccessEnabled + authenticationEnabled supportedLanguages { isoCode displayName diff --git a/webapp/packages/core/src/root/usePermission.ts b/webapp/packages/core/src/root/usePermission.ts new file mode 100644 index 0000000000..8f078e6f89 --- /dev/null +++ b/webapp/packages/core/src/root/usePermission.ts @@ -0,0 +1,9 @@ +import { useService } from '@dbeaver/core/di'; + +import { PermissionsService } from './PermissionsService'; + +export function usePermission(key: string) { + const permissionsService = useService(PermissionsService); + + return permissionsService.has(key); +} diff --git a/webapp/packages/core/src/sdk/GQLErrorCatcher.ts b/webapp/packages/core/src/sdk/GQLErrorCatcher.ts new file mode 100644 index 0000000000..380d2f3e87 --- /dev/null +++ b/webapp/packages/core/src/sdk/GQLErrorCatcher.ts @@ -0,0 +1,34 @@ +/* + * 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 { observable, action } from 'mobx'; + +import { GQLError } from './GQLError'; + +export class GQLErrorCatcher { + @observable hasDetails = false + @observable responseMessage: string | null = null + @observable exception: GQLError | null = null; + + @action catch(exception: any): boolean { + if (exception instanceof GQLError) { + this.responseMessage = exception.errorText; + this.hasDetails = exception.hasDetails(); + this.exception = exception; + return true; + } + this.clear(); + return false; + } + + @action clear() { + this.hasDetails = false; + this.responseMessage = null; + this.exception = null; + } +} diff --git a/webapp/packages/core/src/sdk/ServerInternalError.ts b/webapp/packages/core/src/sdk/ServerInternalError.ts index 1f419194ba..4b8b1c7879 100644 --- a/webapp/packages/core/src/sdk/ServerInternalError.ts +++ b/webapp/packages/core/src/sdk/ServerInternalError.ts @@ -11,10 +11,12 @@ import { ServerError } from './sdk'; export class ServerInternalError extends Error implements ServerError { readonly errorCode?: string; readonly stackTrace?: string; + readonly causedBy?: ServerError; constructor(error: ServerError) { super(error.message); this.errorCode = error.errorCode; this.stackTrace = error.stackTrace; + this.causedBy = error.causedBy; } } diff --git a/webapp/packages/core/src/sdk/index.ts b/webapp/packages/core/src/sdk/index.ts index 11de1dd097..2b9ebec5ed 100644 --- a/webapp/packages/core/src/sdk/index.ts +++ b/webapp/packages/core/src/sdk/index.ts @@ -5,3 +5,4 @@ export * from './GQLError'; export * from './ServerInternalError'; export * from './CachedResource'; export * from './EServerErrorCode'; +export * from './GQLErrorCatcher'; diff --git a/webapp/packages/core/src/sdk/sdk.ts b/webapp/packages/core/src/sdk/sdk.ts index be3a18b4cc..bebdd4e831 100644 --- a/webapp/packages/core/src/sdk/sdk.ts +++ b/webapp/packages/core/src/sdk/sdk.ts @@ -1,7 +1,6 @@ -/* eslint-disable */ -import { GraphQLClient } from "graphql-request"; -import { print } from "graphql"; -import gql from "graphql-tag"; +/* eslint-disable max-len */ +import { GraphQLClient } from 'graphql-request'; + export type Maybe = T; /** All built-in and custom scalars, mapped to their actual values */ @@ -11,1707 +10,1323 @@ export type Scalars = { Boolean: boolean; Int: number; Float: number; - /** Any object (JSON) */ Object: any; - /** Date/Time */ DateTime: any; }; + +export type Query = { + authLogin: UserAuthInfo; + authLogout?: Maybe; + authProviders: Array; + connectionState: ConnectionInfo; + dataSourceList: Array; + dataTransferAvailableStreamProcessors: Array; + dataTransferExportDataFromContainer: AsyncTaskInfo; + dataTransferExportDataFromResults: AsyncTaskInfo; + dataTransferRemoveDataFile?: Maybe; + driverList: Array; + metadataGetNodeDDL?: Maybe; + navGetStructContainers: DatabaseStructContainers; + navNodeChildren: Array; + navNodeInfo: NavigatorNodeInfo; + navRefreshNode?: Maybe; + readSessionLog: Array; + serverConfig: ServerConfig; + sessionPermissions: Array>; + sessionState: SessionInfo; + sessionUser?: Maybe; + sqlCompletionProposals?: Maybe>>; + sqlDialectInfo?: Maybe; + sqlListContexts?: Maybe>>; +}; + + +export type QueryAuthLoginArgs = { + provider: Scalars['ID']; + credentials: Scalars['Object']; +}; + + +export type QueryConnectionStateArgs = { + id: Scalars['ID']; +}; + + +export type QueryDataTransferExportDataFromContainerArgs = { + connectionId: Scalars['ID']; + containerNodePath: Scalars['ID']; + parameters: DataTransferParameters; +}; + + +export type QueryDataTransferExportDataFromResultsArgs = { + connectionId: Scalars['ID']; + contextId: Scalars['ID']; + resultsId: Scalars['ID']; + parameters: DataTransferParameters; +}; + + +export type QueryDataTransferRemoveDataFileArgs = { + dataFileId: Scalars['String']; +}; + + +export type QueryDriverListArgs = { + id?: Maybe; +}; + + +export type QueryMetadataGetNodeDdlArgs = { + nodeId: Scalars['ID']; + options?: Maybe; +}; + + +export type QueryNavGetStructContainersArgs = { + connectionId: Scalars['ID']; + catalog?: Maybe; +}; + + +export type QueryNavNodeChildrenArgs = { + parentPath: Scalars['ID']; + offset?: Maybe; + limit?: Maybe; + onlyFolders?: Maybe; +}; + + +export type QueryNavNodeInfoArgs = { + nodePath: Scalars['ID']; +}; + + +export type QueryNavRefreshNodeArgs = { + nodePath: Scalars['ID']; +}; + + +export type QueryReadSessionLogArgs = { + maxEntries?: Maybe; + clearEntries?: Maybe; +}; + + +export type QuerySqlCompletionProposalsArgs = { + connectionId: Scalars['ID']; + contextId: Scalars['ID']; + query: Scalars['String']; + position: Scalars['Int']; + maxResults?: Maybe; +}; + + +export type QuerySqlDialectInfoArgs = { + connectionId: Scalars['ID']; +}; + + +export type QuerySqlListContextsArgs = { + connectionId: Scalars['ID']; +}; + +export type Mutation = { + asyncSqlExecuteQuery: AsyncTaskInfo; + asyncTaskCancel?: Maybe; + asyncTaskStatus: AsyncTaskInfo; + changeSessionLanguage?: Maybe; + closeConnection: Scalars['Boolean']; + closeSession?: Maybe; + createConnection: ConnectionInfo; + openConnection: ConnectionInfo; + openSession: SessionInfo; + readDataFromContainer?: Maybe; + sqlContextCreate: SqlContextInfo; + sqlContextDestroy: Scalars['Boolean']; + sqlContextSetDefaults: Scalars['Boolean']; + sqlExecuteQuery?: Maybe; + sqlResultClose: Scalars['Boolean']; + testConnection: ConnectionInfo; + touchSession?: Maybe; + updateResultsData?: Maybe; +}; + + +export type MutationAsyncSqlExecuteQueryArgs = { + connectionId: Scalars['ID']; + contextId: Scalars['ID']; + sql: Scalars['String']; + filter?: Maybe; +}; + + +export type MutationAsyncTaskCancelArgs = { + id: Scalars['String']; +}; + + +export type MutationAsyncTaskStatusArgs = { + id: Scalars['String']; +}; + + +export type MutationChangeSessionLanguageArgs = { + locale?: Maybe; +}; + + +export type MutationCloseConnectionArgs = { + id: Scalars['ID']; +}; + + +export type MutationCreateConnectionArgs = { + config: ConnectionConfig; +}; + + +export type MutationOpenConnectionArgs = { + config: ConnectionConfig; +}; + + +export type MutationReadDataFromContainerArgs = { + connectionId: Scalars['ID']; + contextId: Scalars['ID']; + containerNodePath: Scalars['ID']; + filter?: Maybe; +}; + + +export type MutationSqlContextCreateArgs = { + connectionId: Scalars['ID']; + defaultCatalog?: Maybe; + defaultSchema?: Maybe; +}; + + +export type MutationSqlContextDestroyArgs = { + connectionId: Scalars['ID']; + contextId: Scalars['ID']; +}; + + +export type MutationSqlContextSetDefaultsArgs = { + connectionId: Scalars['ID']; + contextId: Scalars['ID']; + defaultCatalog?: Maybe; + defaultSchema?: Maybe; +}; + + +export type MutationSqlExecuteQueryArgs = { + connectionId: Scalars['ID']; + contextId: Scalars['ID']; + sql: Scalars['String']; + filter?: Maybe; +}; + + +export type MutationSqlResultCloseArgs = { + connectionId: Scalars['ID']; + contextId: Scalars['ID']; + resultId: Scalars['ID']; +}; + + +export type MutationTestConnectionArgs = { + config: ConnectionConfig; +}; + + +export type MutationUpdateResultsDataArgs = { + connectionId: Scalars['ID']; + contextId: Scalars['ID']; + resultsId: Scalars['ID']; + updateRow: Array>; + updateValues?: Maybe; +}; + export type AsyncTaskInfo = { - id: Scalars["String"]; - name?: Maybe; - running: Scalars["Boolean"]; - status?: Maybe; + id: Scalars['String']; + name?: Maybe; + running: Scalars['Boolean']; + status?: Maybe; error?: Maybe; result?: Maybe; - /** - * Task result. - * Can be some kind of identifier to obtain real result using another API function - */ - taskResult?: Maybe; + taskResult?: Maybe; +}; + +export type ServerError = { + message?: Maybe; + errorCode?: Maybe; + stackTrace?: Maybe; + causedBy?: Maybe; +}; + +export type ServerMessage = { + time?: Maybe; + message?: Maybe; +}; + +export type ServerLanguage = { + isoCode: Scalars['String']; + displayName?: Maybe; + nativeName?: Maybe; +}; + +export type WebServiceConfig = { + id: Scalars['String']; + name: Scalars['String']; + description: Scalars['String']; + bundleVersion: Scalars['String']; +}; + +export type ServerConfig = { + name: Scalars['String']; + version: Scalars['String']; + anonymousAccessEnabled?: Maybe; + authenticationEnabled?: Maybe; + supportsPredefinedConnections?: Maybe; + supportsProvidedConnections?: Maybe; + supportsCustomConnections?: Maybe; + supportsConnectionBrowser?: Maybe; + supportsWorkspaces?: Maybe; + supportedLanguages: Array; + services?: Maybe>>; + productConfiguration: Scalars['Object']; +}; + +export type SessionInfo = { + createTime: Scalars['String']; + lastAccessTime: Scalars['String']; + locale: Scalars['String']; + cacheExpired: Scalars['Boolean']; + serverMessages?: Maybe>>; + connections: Array; +}; + +export type DriverInfo = { + id: Scalars['ID']; + name?: Maybe; + description?: Maybe; + icon?: Maybe; + iconBig?: Maybe; + providerId?: Maybe; + driverClassName?: Maybe; + defaultPort?: Maybe; + sampleURL?: Maybe; + driverInfoURL?: Maybe; + driverPropertiesURL?: Maybe; + embedded?: Maybe; + anonymousAccess?: Maybe; + allowsEmptyPassword?: Maybe; + licenseRequired?: Maybe; + license?: Maybe; + custom?: Maybe; + promotedScore?: Maybe; + connectionProperties?: Maybe; + defaultConnectionProperties?: Maybe; + driverProperties?: Maybe>>; + driverParameters?: Maybe; +}; + +export type DriverPropertyInfo = { + id: Scalars['ID']; + displayName?: Maybe; + description?: Maybe; + category?: Maybe; + dataType?: Maybe; + defaultValue?: Maybe; + validValues?: Maybe>>; +}; + +export type DataSourceInfo = { + id: Scalars['ID']; + driverId: Scalars['ID']; + name: Scalars['String']; + description?: Maybe; + host?: Maybe; + server?: Maybe; + port?: Maybe; + url?: Maybe; + properties?: Maybe; }; -/** Configuration of particular connection. Used for new connection create. Includes auth info */ export type ConnectionConfig = { - name?: Maybe; - description?: Maybe; - /** ID of predefined datasource */ - dataSourceId?: Maybe; - /** Driver ID */ - driverId?: Maybe; - /** Host */ - host?: Maybe; - /** Port */ - port?: Maybe; - /** Databae name */ - databaseName?: Maybe; - /** Databae name */ - url?: Maybe; - /** Properties */ - properties?: Maybe; - userName?: Maybe; - userPassword?: Maybe; + name?: Maybe; + description?: Maybe; + dataSourceId?: Maybe; + driverId?: Maybe; + host?: Maybe; + port?: Maybe; + databaseName?: Maybe; + url?: Maybe; + properties?: Maybe; + userName?: Maybe; + userPassword?: Maybe; }; -/** Connection instance */ export type ConnectionInfo = { - id: Scalars["ID"]; - driverId: Scalars["ID"]; - name: Scalars["String"]; - description?: Maybe; - properties?: Maybe; - connected: Scalars["Boolean"]; - provided: Scalars["Boolean"]; - connectTime?: Maybe; + id: Scalars['ID']; + driverId: Scalars['ID']; + name: Scalars['String']; + description?: Maybe; + properties?: Maybe; + connected: Scalars['Boolean']; + provided: Scalars['Boolean']; + connectTime?: Maybe; connectionError?: Maybe; - serverVersion?: Maybe; - clientVersion?: Maybe; - /** Supported features (provided etc) */ - features?: Maybe>; + serverVersion?: Maybe; + clientVersion?: Maybe; + features: Array; +}; + +export type LogEntry = { + time?: Maybe; + type: Scalars['String']; + message?: Maybe; + stackTrace?: Maybe; +}; + +export type ObjectDescriptor = { + id?: Maybe; + displayName?: Maybe; + fullName?: Maybe; + uniqueName?: Maybe; + description?: Maybe; + value?: Maybe; +}; + +export type ObjectPropertyInfo = { + id?: Maybe; + displayName?: Maybe; + description?: Maybe; + category?: Maybe; + dataType?: Maybe; + value?: Maybe; + validValues?: Maybe>>; + defaultValue?: Maybe; + features: Array; +}; + +export type ObjectPropertyFilter = { + ids?: Maybe>>; + features?: Maybe>>; + categories?: Maybe>>; + dataTypes?: Maybe>>; }; export type DatabaseObjectInfo = { - /** Object name */ - name?: Maybe; - /** Description - optional */ - description?: Maybe; - /** Object type. Java class name in most cases */ - type?: Maybe; - /** - * Read object properties. - * Optional parameter 'ids' filters properties by id. null means all properties. - * Note: property value reading may take a lot of time so don't read all property values always - * Examine property meta (features in particular) before reading them - */ + name?: Maybe; + description?: Maybe; + type?: Maybe; properties?: Maybe>>; - ordinalPosition?: Maybe; - fullyQualifiedName?: Maybe; - overloadedName?: Maybe; - uniqueName?: Maybe; - state?: Maybe; - /** Features: script, scriptExtended, dataContainer, dataManipulator, entity, schema, catalog */ - features?: Maybe>>; - /** Supported editors: ddl, permissions, sourceDeclaration, sourceDefinition */ - editors?: Maybe>>; + ordinalPosition?: Maybe; + fullyQualifiedName?: Maybe; + overloadedName?: Maybe; + uniqueName?: Maybe; + state?: Maybe; + features?: Maybe>>; + editors?: Maybe>>; }; + export type DatabaseObjectInfoPropertiesArgs = { filter?: Maybe; }; +export type NavigatorNodeInfo = { + id: Scalars['ID']; + name?: Maybe; + icon?: Maybe; + description?: Maybe; + nodeType?: Maybe; + hasChildren?: Maybe; + object?: Maybe; + features?: Maybe>>; + folder?: Maybe; + inline?: Maybe; + navigable?: Maybe; +}; + export type DatabaseStructContainers = { catalogList: Array; schemaList: Array; }; -/** Data source info is a description of some remote database. Doesn't include user credentials */ -export type DataSourceInfo = { - id: Scalars["ID"]; - driverId: Scalars["ID"]; - name: Scalars["String"]; - description?: Maybe; - host?: Maybe; - server?: Maybe; - port?: Maybe; - url?: Maybe; - properties?: Maybe; -}; - -export type DataTransferParameters = { - /** Processor ID */ - processorId: Scalars["ID"]; - /** - * General settings: - * - openNewConnection: opens new database connection for data transfer task - */ - settings?: Maybe; - /** Processor properties. See DataTransferProcessorInfo.properties */ - processorProperties: Scalars["Object"]; - /** Data filter settings */ - filter?: Maybe; -}; - -export type DataTransferProcessorInfo = { - id: Scalars["ID"]; - name?: Maybe; - description?: Maybe; - fileExtension?: Maybe; - appFileExtension?: Maybe; - appName?: Maybe; - order: Scalars["Int"]; - icon?: Maybe; - properties?: Maybe>>; - isBinary?: Maybe; - isHTML?: Maybe; -}; - -export type DriverInfo = { - id: Scalars["ID"]; - name?: Maybe; - description?: Maybe; - icon?: Maybe; - iconBig?: Maybe; - /** Driver provider ID */ - providerId?: Maybe; - /** Driver Java class name */ - driverClassName?: Maybe; - defaultPort?: Maybe; - sampleURL?: Maybe; - driverInfoURL?: Maybe; - driverPropertiesURL?: Maybe; - embedded?: Maybe; - anonymousAccess?: Maybe; - allowsEmptyPassword?: Maybe; - licenseRequired?: Maybe; - license?: Maybe; - custom?: Maybe; - /** Driver score for ordering, biggest first */ - promotedScore?: Maybe; - connectionProperties?: Maybe; - defaultConnectionProperties?: Maybe; - /** - * Driver properties. - * Note: it is expensive property and it may produce database server roundtrips. - * Call it only when you really need it. - */ - driverProperties?: Maybe>>; - driverParameters?: Maybe; -}; - -export type DriverPropertyInfo = { - id: Scalars["ID"]; - displayName?: Maybe; - description?: Maybe; - category?: Maybe; - dataType?: Maybe; - defaultValue?: Maybe; - validValues?: Maybe>>; -}; - -export type LogEntry = { - time?: Maybe; - type: Scalars["String"]; - message?: Maybe; - stackTrace?: Maybe; -}; - -export type Mutation = { - /** Initialize session */ - openSession?: Maybe; - /** Destroy session */ - closeSession?: Maybe; - /** Refreshes session on server and returns its state */ - touchSession?: Maybe; - /** Refreshes session on server and returns its state */ - changeSessionLanguage?: Maybe; - /** Create new connection */ - createConnection?: Maybe; - /** Test connection configuration. Returns remote server version */ - testConnection?: Maybe; - /** Connect to database */ - openConnection?: Maybe; - /** Disconnect from database */ - closeConnection: Scalars["Boolean"]; - asyncTaskCancel?: Maybe; - asyncTaskStatus: AsyncTaskInfo; - sqlContextCreate: SqlContextInfo; - sqlContextSetDefaults: Scalars["Boolean"]; - sqlContextDestroy: Scalars["Boolean"]; - sqlExecuteQuery?: Maybe; - sqlResultClose: Scalars["Boolean"]; - readDataFromContainer?: Maybe; - updateResultsData?: Maybe; - /** Returns SQLExecuteInfo */ - asyncSqlExecuteQuery: AsyncTaskInfo; -}; - -export type MutationChangeSessionLanguageArgs = { - locale?: Maybe; -}; - -export type MutationCreateConnectionArgs = { - config: ConnectionConfig; -}; - -export type MutationTestConnectionArgs = { - config: ConnectionConfig; -}; - -export type MutationOpenConnectionArgs = { - config: ConnectionConfig; -}; - -export type MutationCloseConnectionArgs = { - id: Scalars["ID"]; -}; - -export type MutationAsyncTaskCancelArgs = { - id: Scalars["String"]; -}; - -export type MutationAsyncTaskStatusArgs = { - id: Scalars["String"]; -}; - -export type MutationSqlContextCreateArgs = { - connectionId: Scalars["ID"]; - defaultCatalog?: Maybe; - defaultSchema?: Maybe; -}; - -export type MutationSqlContextSetDefaultsArgs = { - connectionId: Scalars["ID"]; - contextId: Scalars["ID"]; - defaultCatalog?: Maybe; - defaultSchema?: Maybe; -}; - -export type MutationSqlContextDestroyArgs = { - connectionId: Scalars["ID"]; - contextId: Scalars["ID"]; -}; - -export type MutationSqlExecuteQueryArgs = { - connectionId: Scalars["ID"]; - contextId: Scalars["ID"]; - sql: Scalars["String"]; - filter?: Maybe; -}; - -export type MutationSqlResultCloseArgs = { - connectionId: Scalars["ID"]; - contextId: Scalars["ID"]; - resultId: Scalars["ID"]; -}; - -export type MutationReadDataFromContainerArgs = { - connectionId: Scalars["ID"]; - contextId: Scalars["ID"]; - containerNodePath: Scalars["ID"]; - filter?: Maybe; -}; - -export type MutationUpdateResultsDataArgs = { - connectionId: Scalars["ID"]; - contextId: Scalars["ID"]; - resultsId: Scalars["ID"]; - updateRow: Array>; - updateValues?: Maybe; -}; - -export type MutationAsyncSqlExecuteQueryArgs = { - connectionId: Scalars["ID"]; - contextId: Scalars["ID"]; - sql: Scalars["String"]; - filter?: Maybe; -}; - -export type NavigatorNodeInfo = { - /** Node ID - generally a full path to the node from root of tree */ - id: Scalars["ID"]; - /** Node human readable name */ - name?: Maybe; - /** Node icon path */ - icon?: Maybe; - /** Node description */ - description?: Maybe; - /** Node type */ - nodeType?: Maybe; - /** Can this property have child nodes? */ - hasChildren?: Maybe; - /** Associated object. Maybe null for non-database objects */ - object?: Maybe; - /** Supported features: item, container, leaf */ - features?: Maybe>>; - folder?: Maybe; - inline?: Maybe; - navigable?: Maybe; -}; - -export type ObjectDescriptor = { - id?: Maybe; - displayName?: Maybe; - fullName?: Maybe; - uniqueName?: Maybe; - description?: Maybe; - value?: Maybe; -}; - -export type ObjectPropertyFilter = { - ids?: Maybe>>; - features?: Maybe>>; - categories?: Maybe>>; - dataTypes?: Maybe>>; -}; - -export type ObjectPropertyInfo = { - /** ID */ - id?: Maybe; - /** Human readable name */ - displayName?: Maybe; - /** Property description */ - description?: Maybe; - /** Property category (may be used if object has a lot of properties) */ - category?: Maybe; - /** Property data type (int, String, etc) */ - dataType?: Maybe; - /** Property value. Note: for some properties value reading may take a lot of time (e.g. RowCount for tables) */ - value?: Maybe; - /** List of values this property can take. Makes sense only for enumerable properties */ - validValues?: Maybe>>; - /** Default property value */ - defaultValue?: Maybe; - /** Supported features (system, hidden, inherited, foreign, expensive, etc) */ - features?: Maybe>; -}; - -export type Query = { - /** Returns server config */ - serverConfig?: Maybe; - /** Returns session state ( initialize if not ) */ - sessionState?: Maybe; - /** Session permissions */ - sessionPermissions: Array>; - /** Get driver info */ - driverList?: Maybe>; - /** Get list of predefined data sources */ - dataSourceList?: Maybe>; - /** Return connection state */ - connectionState?: Maybe; - readSessionLog?: Maybe>; - /** Get child nodes */ - navNodeChildren?: Maybe>; - navNodeInfo?: Maybe; - navRefreshNode?: Maybe; - navGetStructContainers: DatabaseStructContainers; - sqlDialectInfo?: Maybe; - sqlListContexts?: Maybe>>; - sqlCompletionProposals?: Maybe>>; - /** Available transfer processors */ - dataTransferAvailableStreamProcessors?: Maybe< - Array> - >; - dataTransferExportDataFromContainer: AsyncTaskInfo; - dataTransferExportDataFromResults: AsyncTaskInfo; - dataTransferRemoveDataFile?: Maybe; - /** Get child nodes */ - metadataGetNodeDDL?: Maybe; -}; - -export type QueryDriverListArgs = { - id?: Maybe; -}; - -export type QueryConnectionStateArgs = { - id: Scalars["ID"]; -}; - -export type QueryReadSessionLogArgs = { - maxEntries?: Maybe; - clearEntries?: Maybe; -}; - -export type QueryNavNodeChildrenArgs = { - parentPath: Scalars["ID"]; - offset?: Maybe; - limit?: Maybe; - onlyFolders?: Maybe; -}; - -export type QueryNavNodeInfoArgs = { - nodePath: Scalars["ID"]; -}; - -export type QueryNavRefreshNodeArgs = { - nodePath: Scalars["ID"]; -}; - -export type QueryNavGetStructContainersArgs = { - connectionId: Scalars["ID"]; - catalog?: Maybe; -}; - -export type QuerySqlDialectInfoArgs = { - connectionId: Scalars["ID"]; -}; - -export type QuerySqlListContextsArgs = { - connectionId: Scalars["ID"]; -}; - -export type QuerySqlCompletionProposalsArgs = { - connectionId: Scalars["ID"]; - contextId: Scalars["ID"]; - query: Scalars["String"]; - position: Scalars["Int"]; - maxResults?: Maybe; -}; - -export type QueryDataTransferExportDataFromContainerArgs = { - connectionId: Scalars["ID"]; - containerNodePath: Scalars["ID"]; - parameters: DataTransferParameters; -}; - -export type QueryDataTransferExportDataFromResultsArgs = { - connectionId: Scalars["ID"]; - contextId: Scalars["ID"]; - resultsId: Scalars["ID"]; - parameters: DataTransferParameters; -}; - -export type QueryDataTransferRemoveDataFileArgs = { - dataFileId: Scalars["String"]; -}; - -export type QueryMetadataGetNodeDdlArgs = { - nodeId: Scalars["ID"]; - options?: Maybe; -}; - -export type ServerConfig = { - name: Scalars["String"]; - version: Scalars["String"]; - anonymousAccessEnabled?: Maybe; - authenticationEnabled?: Maybe; - supportsPredefinedConnections?: Maybe; - supportsProvidedConnections?: Maybe; - supportsCustomConnections?: Maybe; - supportsConnectionBrowser?: Maybe; - supportsWorkspaces?: Maybe; - supportedLanguages: Array>; - services?: Maybe>>; - productConfiguration: Scalars["Object"]; -}; - -export type ServerError = { - message?: Maybe; - errorCode?: Maybe; - stackTrace?: Maybe; - causedBy?: Maybe; -}; - -export type ServerLanguage = { - isoCode: Scalars["String"]; - displayName?: Maybe; - nativeName?: Maybe; -}; - -export type ServerMessage = { - time?: Maybe; - message?: Maybe; -}; - -export type SessionInfo = { - createTime: Scalars["String"]; - lastAccessTime: Scalars["String"]; - locale: Scalars["String"]; - cacheExpired: Scalars["Boolean"]; - serverMessages?: Maybe>>; - connections: Array>; +export type SqlDialectInfo = { + name?: Maybe; + dataTypes?: Maybe>>; + functions?: Maybe>>; + reservedWords?: Maybe>>; + quoteStrings?: Maybe>>>>; + singleLineComments?: Maybe>>; + multiLineComments?: Maybe>>>>; + catalogSeparator?: Maybe; + structSeparator?: Maybe; + scriptDelimiter?: Maybe; }; export type SqlCompletionProposal = { - displayString?: Maybe; - type?: Maybe; - score?: Maybe; - replacementString?: Maybe; - replacementOffset?: Maybe; - replacementLength?: Maybe; - cursorPosition?: Maybe; - icon?: Maybe; - nodePath?: Maybe; + displayString?: Maybe; + type?: Maybe; + score?: Maybe; + replacementString?: Maybe; + replacementOffset?: Maybe; + replacementLength?: Maybe; + cursorPosition?: Maybe; + icon?: Maybe; + nodePath?: Maybe; }; -/** SQL context must be created for each SQL editor */ export type SqlContextInfo = { - id: Scalars["ID"]; - defaultCatalog?: Maybe; - defaultSchema?: Maybe; -}; - -export type SqlDataFilter = { - offset?: Maybe; - limit?: Maybe; - constraints?: Maybe>>; - where?: Maybe; + id: Scalars['ID']; + defaultCatalog?: Maybe; + defaultSchema?: Maybe; }; export type SqlDataFilterConstraint = { - attribute: Scalars["String"]; - orderPosition?: Maybe; - orderAsc?: Maybe; - criteria?: Maybe; - operator?: Maybe; - value?: Maybe; + attribute: Scalars['String']; + orderPosition?: Maybe; + orderAsc?: Maybe; + criteria?: Maybe; + operator?: Maybe; + value?: Maybe; }; -export type SqlDialectInfo = { - name?: Maybe; - dataTypes?: Maybe>>; - functions?: Maybe>>; - reservedWords?: Maybe>>; - quoteStrings?: Maybe>>>>; - singleLineComments?: Maybe>>; - multiLineComments?: Maybe>>>>; - catalogSeparator?: Maybe; - structSeparator?: Maybe; - scriptDelimiter?: Maybe; -}; - -export type SqlExecuteInfo = { - statusMessage?: Maybe; - duration?: Maybe; - results?: Maybe>; -}; - -export type SqlQueryResults = { - title?: Maybe; - updateRowCount?: Maybe; - sourceQuery?: Maybe; - resultSet?: Maybe; +export type SqlDataFilter = { + offset?: Maybe; + limit?: Maybe; + constraints?: Maybe>>; + where?: Maybe; }; export type SqlResultColumn = { - position?: Maybe; - name?: Maybe; - label?: Maybe; - icon?: Maybe; - entityName?: Maybe; - dataKind?: Maybe; - typeName?: Maybe; - fullTypeName?: Maybe; - maxLength?: Maybe; - scale?: Maybe; - precision?: Maybe; + position?: Maybe; + name?: Maybe; + label?: Maybe; + icon?: Maybe; + entityName?: Maybe; + dataKind?: Maybe; + typeName?: Maybe; + fullTypeName?: Maybe; + maxLength?: Maybe; + scale?: Maybe; + precision?: Maybe; }; export type SqlResultSet = { - id: Scalars["ID"]; + id: Scalars['ID']; columns?: Maybe>>; - rows?: Maybe>>>>; - /** server always returns hasMoreData = false */ - hasMoreData?: Maybe; + rows?: Maybe>>>>; + hasMoreData?: Maybe; }; -export type WebServiceConfig = { - id: Scalars["String"]; - name: Scalars["String"]; - description: Scalars["String"]; - bundleVersion: Scalars["String"]; +export type SqlQueryResults = { + title?: Maybe; + updateRowCount?: Maybe; + sourceQuery?: Maybe; + resultSet?: Maybe; +}; + +export type SqlExecuteInfo = { + statusMessage?: Maybe; + duration?: Maybe; + results: Array; +}; + +export enum AuthCredentialEncryption { + None = 'none', + Plain = 'plain', + Hash = 'hash' +} + +export type AuthCredentialInfo = { + id: Scalars['ID']; + displayName: Scalars['String']; + description?: Maybe; + admin?: Maybe; + user?: Maybe; + possibleValues?: Maybe>>; + encryption?: Maybe; +}; + +export type AuthProviderInfo = { + id: Scalars['ID']; + label: Scalars['String']; + icon?: Maybe; + description?: Maybe; + defaultProvider?: Maybe; + credentialParameters: Array; +}; + +export type UserAuthInfo = { + userId: Scalars['String']; + displayName?: Maybe; + authProvider: Scalars['String']; + loginTime: Scalars['DateTime']; + message?: Maybe; +}; + +export type DataTransferProcessorInfo = { + id: Scalars['ID']; + name?: Maybe; + description?: Maybe; + fileExtension?: Maybe; + appFileExtension?: Maybe; + appName?: Maybe; + order: Scalars['Int']; + icon?: Maybe; + properties?: Maybe>>; + isBinary?: Maybe; + isHTML?: Maybe; +}; + +export type DataTransferParameters = { + processorId: Scalars['ID']; + settings?: Maybe; + processorProperties: Scalars['Object']; + filter?: Maybe; }; export type NavGetStructContainersQueryVariables = { - connectionId: Scalars["ID"]; - catalogId?: Maybe; + connectionId: Scalars['ID']; + catalogId?: Maybe; }; -export type NavGetStructContainersQuery = { - navGetStructContainers: { - catalogList: Array< - Pick - >; - schemaList: Array< - Pick - >; - }; -}; + +export type NavGetStructContainersQuery = { navGetStructContainers: { catalogList: Array>; schemaList: Array> } }; export type CloseConnectionMutationVariables = { - id: Scalars["ID"]; + id: Scalars['ID']; }; -export type CloseConnectionMutation = Pick; + +export type CloseConnectionMutation = Pick; export type ConnectionStateQueryVariables = { - id: Scalars["ID"]; + id: Scalars['ID']; }; -export type ConnectionStateQuery = { - connection: Maybe< - Pick - >; -}; + +export type ConnectionStateQuery = { connection: Pick }; export type CreateConnectionMutationVariables = { config: ConnectionConfig; }; -export type CreateConnectionMutation = { - createConnection: Maybe< - Pick - >; -}; + +export type CreateConnectionMutation = { createConnection: Pick }; export type DataSourceListQueryVariables = {}; -export type DataSourceListQuery = { - dataSourceList: Maybe< - Array> - >; -}; + +export type DataSourceListQuery = { dataSourceList: Array> }; export type DriverListQueryVariables = {}; -export type DriverListQuery = { - driverList: Maybe< - Array< - Pick< - DriverInfo, - | "id" - | "name" - | "icon" - | "description" - | "defaultPort" - | "sampleURL" - | "embedded" - | "anonymousAccess" - | "promotedScore" - > - > - >; -}; + +export type DriverListQuery = { driverList: Array> }; export type DriverPropertiesQueryVariables = { - driverId: Scalars["ID"]; + driverId: Scalars['ID']; }; -export type DriverPropertiesQuery = { - driver: Maybe< - Array< - Pick & { - driverProperties: Maybe< - Array< - Maybe< - Pick< - DriverPropertyInfo, - | "id" - | "displayName" - | "description" - | "category" - | "dataType" - | "defaultValue" - | "validValues" - > - > - > - >; - } - > - >; -}; + +export type DriverPropertiesQuery = { driver: Array<( + Pick + & { driverProperties?: Maybe>>> } + )>; }; export type GetDriverByIdQueryVariables = { - driverId: Scalars["ID"]; + driverId: Scalars['ID']; }; -export type GetDriverByIdQuery = { - driverList: Maybe>>; -}; + +export type GetDriverByIdQuery = { driverList: Array> }; export type OpenConnectionMutationVariables = { config: ConnectionConfig; }; -export type OpenConnectionMutation = { - openConnection: Maybe< - Pick - >; -}; + +export type OpenConnectionMutation = { openConnection: Pick }; export type TestConnectionMutationVariables = { config: ConnectionConfig; }; -export type TestConnectionMutation = { - testConnection: Maybe>; -}; + +export type TestConnectionMutation = { testConnection: Pick }; export type NavNodeChildrenQueryVariables = { - parentPath: Scalars["ID"]; + parentPath: Scalars['ID']; }; -export type NavNodeChildrenQuery = { - navNodeChildren: Maybe< - Array< - Pick< - NavigatorNodeInfo, - | "id" - | "name" - | "hasChildren" - | "nodeType" - | "icon" - | "folder" - | "inline" - | "navigable" - | "features" - > & { object: Maybe> } - > - >; -}; + +export type NavNodeChildrenQuery = { navNodeChildren: Array<( + Pick + & { object?: Maybe> } + )>; }; export type NavNodeInfoQueryVariables = { - nodePath: Scalars["ID"]; + nodePath: Scalars['ID']; }; -export type NavNodeInfoQuery = { - navNodeInfo: Maybe< - Pick< - NavigatorNodeInfo, - | "id" - | "name" - | "hasChildren" - | "nodeType" - | "icon" - | "folder" - | "inline" - | "navigable" - | "features" - > & { object: Maybe> } - >; -}; + +export type NavNodeInfoQuery = { navNodeInfo: ( + Pick + & { object?: Maybe> } + ); }; export type QueryChildrenDatabaseObjectInfoQueryVariables = { - nodePath: Scalars["ID"]; + nodePath: Scalars['ID']; filter?: Maybe; }; -export type QueryChildrenDatabaseObjectInfoQuery = { - childrenDatabaseObjectInfo: Maybe< - Array< - Pick & { - object: Maybe< - Pick & { - properties: Maybe< - Array< - Maybe< - Pick< - ObjectPropertyInfo, - | "id" - | "category" - | "dataType" - | "description" - | "displayName" - | "features" - | "value" - > - > - > - >; - } - >; - } - > - >; -}; + +export type QueryChildrenDatabaseObjectInfoQuery = { childrenDatabaseObjectInfo: Array<( + Pick + & { object?: Maybe<( + Pick + & { properties?: Maybe>>> } + )>; } + )>; }; export type QueryDatabaseObjectInfoQueryVariables = { - nodeId: Scalars["ID"]; + nodeId: Scalars['ID']; filter?: Maybe; }; -export type QueryDatabaseObjectInfoQuery = { - objectInfo: Maybe< - Pick & { - object: Maybe< - Pick & { - properties: Maybe< - Array< - Maybe< - Pick< - ObjectPropertyInfo, - | "id" - | "category" - | "dataType" - | "description" - | "displayName" - | "features" - | "value" - > - > - > - >; - } - >; - } - >; -}; + +export type QueryDatabaseObjectInfoQuery = { objectInfo: ( + Pick + & { object?: Maybe<( + Pick + & { properties?: Maybe>>> } + )>; } + ); }; export type ReadSessionLogQueryVariables = { - maxEntries: Scalars["Int"]; - clearEntries: Scalars["Boolean"]; + maxEntries: Scalars['Int']; + clearEntries: Scalars['Boolean']; }; -export type ReadSessionLogQuery = { - log: Maybe>>; -}; + +export type ReadSessionLogQuery = { log: Array> }; export type ChangeSessionLanguageMutationVariables = { - locale: Scalars["String"]; + locale: Scalars['String']; }; -export type ChangeSessionLanguageMutation = Pick< - Mutation, - "changeSessionLanguage" ->; + +export type ChangeSessionLanguageMutation = Pick; + +export type AuthLoginQueryVariables = { + provider: Scalars['ID']; + credentials: Scalars['Object']; +}; + + +export type AuthLoginQuery = { user: Pick }; + +export type AuthLogoutQueryVariables = {}; + + +export type AuthLogoutQuery = Pick; + +export type GetAuthProvidersQueryVariables = {}; + + +export type GetAuthProvidersQuery = { providers: Array<( + Pick + & { credentialParameters: Array> } + )>; }; + +export type GetSessionUserQueryVariables = {}; + + +export type GetSessionUserQuery = { user?: Maybe> }; export type AsyncExportTaskStatusMutationVariables = { - taskId: Scalars["String"]; + taskId: Scalars['String']; }; -export type AsyncExportTaskStatusMutation = { - taskInfo: Pick< - AsyncTaskInfo, - "id" | "name" | "running" | "status" | "taskResult" - > & { - error: Maybe>; - }; -}; + +export type AsyncExportTaskStatusMutation = { taskInfo: ( + Pick + & { error?: Maybe> } + ); }; export type ExportDataFromContainerQueryVariables = { - connectionId: Scalars["ID"]; - containerNodePath: Scalars["ID"]; + connectionId: Scalars['ID']; + containerNodePath: Scalars['ID']; parameters: DataTransferParameters; }; -export type ExportDataFromContainerQuery = { - taskInfo: Pick & { - error: Maybe>; - }; -}; + +export type ExportDataFromContainerQuery = { taskInfo: ( + Pick + & { error?: Maybe> } + ); }; export type ExportDataFromResultsQueryVariables = { - connectionId: Scalars["ID"]; - contextId: Scalars["ID"]; - resultsId: Scalars["ID"]; + connectionId: Scalars['ID']; + contextId: Scalars['ID']; + resultsId: Scalars['ID']; parameters: DataTransferParameters; }; -export type ExportDataFromResultsQuery = { - taskInfo: Pick & { - error: Maybe>; - }; -}; + +export type ExportDataFromResultsQuery = { taskInfo: ( + Pick + & { error?: Maybe> } + ); }; export type GetDataTransferProcessorsQueryVariables = {}; -export type GetDataTransferProcessorsQuery = { - processors: Maybe< - Array< - Maybe< - Pick< - DataTransferProcessorInfo, - | "id" - | "name" - | "description" - | "fileExtension" - | "appFileExtension" - | "appName" - | "order" - | "icon" - | "isBinary" - | "isHTML" - > & { - properties: Maybe< - Array< - Maybe< - Pick< - ObjectPropertyInfo, - | "id" - | "displayName" - | "description" - | "category" - | "dataType" - | "defaultValue" - | "validValues" - | "features" - > - > - > - >; - } - > - > - >; -}; + +export type GetDataTransferProcessorsQuery = { processors: Array<( + Pick + & { properties?: Maybe>>> } + )>; }; export type RemoveDataTransferFileQueryVariables = { - dataFileId: Scalars["String"]; + dataFileId: Scalars['String']; }; -export type RemoveDataTransferFileQuery = { - result: Query["dataTransferRemoveDataFile"]; -}; + +export type RemoveDataTransferFileQuery = { result: Query['dataTransferRemoveDataFile'] }; export type AsyncSqlExecuteQueryMutationVariables = { - connectionId: Scalars["ID"]; - contextId: Scalars["ID"]; - query: Scalars["String"]; + connectionId: Scalars['ID']; + contextId: Scalars['ID']; + query: Scalars['String']; filter?: Maybe; }; -export type AsyncSqlExecuteQueryMutation = { - taskInfo: Pick & { - result: Maybe< - Pick & { - results: Maybe< - Array< - Pick< - SqlQueryResults, - "updateRowCount" | "sourceQuery" | "title" - > & { - resultSet: Maybe< - Pick & { - columns: Maybe< - Array< - Maybe< - Pick< - SqlResultColumn, - | "dataKind" - | "entityName" - | "fullTypeName" - | "icon" - | "label" - | "maxLength" - | "name" - | "position" - | "precision" - | "scale" - | "typeName" - > - > - > - >; - } - >; - } - > - >; - } - >; - error: Maybe>; - }; -}; + +export type AsyncSqlExecuteQueryMutation = { taskInfo: ( + Pick + & { result?: Maybe<( + Pick + & { results: Array<( + Pick + & { resultSet?: Maybe<( + Pick + & { columns?: Maybe>>> } + )>; } + )>; } + )>; error?: Maybe>; } + ); }; export type AsyncTaskCancelMutationVariables = { - taskId: Scalars["String"]; + taskId: Scalars['String']; }; -export type AsyncTaskCancelMutation = { result: Mutation["asyncTaskCancel"] }; + +export type AsyncTaskCancelMutation = { result: Mutation['asyncTaskCancel'] }; export type AsyncTaskStatusMutationVariables = { - taskId: Scalars["String"]; + taskId: Scalars['String']; }; -export type AsyncTaskStatusMutation = { - taskInfo: Pick & { - result: Maybe< - Pick & { - results: Maybe< - Array< - Pick< - SqlQueryResults, - "updateRowCount" | "sourceQuery" | "title" - > & { - resultSet: Maybe< - Pick & { - columns: Maybe< - Array< - Maybe< - Pick< - SqlResultColumn, - | "dataKind" - | "entityName" - | "fullTypeName" - | "icon" - | "label" - | "maxLength" - | "name" - | "position" - | "precision" - | "scale" - | "typeName" - > - > - > - >; - } - >; - } - > - >; - } - >; - error: Maybe>; - }; -}; + +export type AsyncTaskStatusMutation = { taskInfo: ( + Pick + & { result?: Maybe<( + Pick + & { results: Array<( + Pick + & { resultSet?: Maybe<( + Pick + & { columns?: Maybe>>> } + )>; } + )>; } + )>; error?: Maybe>; } + ); }; export type ExecuteSqlQueryMutationVariables = { - connectionId: Scalars["ID"]; - contextId: Scalars["ID"]; - query: Scalars["String"]; + connectionId: Scalars['ID']; + contextId: Scalars['ID']; + query: Scalars['String']; filter?: Maybe; }; -export type ExecuteSqlQueryMutation = { - result: Maybe< - Pick & { - results: Maybe< - Array< - Pick & { - resultSet: Maybe< - Pick & { - columns: Maybe< - Array< - Maybe< - Pick< - SqlResultColumn, - | "dataKind" - | "entityName" - | "fullTypeName" - | "icon" - | "label" - | "maxLength" - | "name" - | "position" - | "precision" - | "scale" - | "typeName" - > - > - > - >; - } - >; - } - > - >; - } - >; -}; + +export type ExecuteSqlQueryMutation = { result?: Maybe<( + Pick + & { results: Array<( + Pick + & { resultSet?: Maybe<( + Pick + & { columns?: Maybe>>> } + )>; } + )>; } + )>; }; export type MetadataGetNodeDdlQueryVariables = { - nodeId: Scalars["ID"]; + nodeId: Scalars['ID']; }; -export type MetadataGetNodeDdlQuery = Pick; + +export type MetadataGetNodeDdlQuery = Pick; export type QuerySqlCompletionProposalsQueryVariables = { - connectionId: Scalars["ID"]; - contextId: Scalars["ID"]; - position: Scalars["Int"]; - query: Scalars["String"]; - maxResults?: Maybe; + connectionId: Scalars['ID']; + contextId: Scalars['ID']; + position: Scalars['Int']; + query: Scalars['String']; + maxResults?: Maybe; }; -export type QuerySqlCompletionProposalsQuery = { - sqlCompletionProposals: Maybe< - Array< - Maybe< - Pick< - SqlCompletionProposal, - | "cursorPosition" - | "displayString" - | "icon" - | "nodePath" - | "replacementLength" - | "replacementOffset" - | "replacementString" - | "score" - | "type" - > - > - > - >; -}; + +export type QuerySqlCompletionProposalsQuery = { sqlCompletionProposals?: Maybe>>> }; export type QuerySqlDialectInfoQueryVariables = { - connectionId: Scalars["ID"]; + connectionId: Scalars['ID']; }; -export type QuerySqlDialectInfoQuery = { - dialect: Maybe< - Pick< - SqlDialectInfo, - | "name" - | "dataTypes" - | "functions" - | "reservedWords" - | "quoteStrings" - | "singleLineComments" - | "multiLineComments" - | "catalogSeparator" - | "structSeparator" - | "scriptDelimiter" - > - >; -}; + +export type QuerySqlDialectInfoQuery = { dialect?: Maybe> }; export type ReadDataFromContainerMutationVariables = { - connectionId: Scalars["ID"]; - contextId: Scalars["ID"]; - containerNodePath: Scalars["ID"]; + connectionId: Scalars['ID']; + contextId: Scalars['ID']; + containerNodePath: Scalars['ID']; filter?: Maybe; }; -export type ReadDataFromContainerMutation = { - readDataFromContainer: Maybe< - Pick & { - results: Maybe< - Array< - Pick & { - resultSet: Maybe< - Pick & { - columns: Maybe< - Array< - Maybe< - Pick< - SqlResultColumn, - | "dataKind" - | "entityName" - | "fullTypeName" - | "icon" - | "label" - | "maxLength" - | "name" - | "position" - | "precision" - | "scale" - | "typeName" - > - > - > - >; - } - >; - } - > - >; - } - >; -}; + +export type ReadDataFromContainerMutation = { readDataFromContainer?: Maybe<( + Pick + & { results: Array<( + Pick + & { resultSet?: Maybe<( + Pick + & { columns?: Maybe>>> } + )>; } + )>; } + )>; }; export type SqlContextCreateMutationVariables = { - connectionId: Scalars["ID"]; - defaultCatalog?: Maybe; - defaultSchema?: Maybe; + connectionId: Scalars['ID']; + defaultCatalog?: Maybe; + defaultSchema?: Maybe; }; -export type SqlContextCreateMutation = { - context: Pick; -}; + +export type SqlContextCreateMutation = { context: Pick }; export type SqlContextDestroyMutationVariables = { - connectionId: Scalars["ID"]; - contextId: Scalars["ID"]; + connectionId: Scalars['ID']; + contextId: Scalars['ID']; }; -export type SqlContextDestroyMutation = Pick; + +export type SqlContextDestroyMutation = Pick; export type SqlContextSetDefaultsMutationVariables = { - connectionId: Scalars["ID"]; - contextId: Scalars["ID"]; - defaultCatalog?: Maybe; - defaultSchema?: Maybe; + connectionId: Scalars['ID']; + contextId: Scalars['ID']; + defaultCatalog?: Maybe; + defaultSchema?: Maybe; }; -export type SqlContextSetDefaultsMutation = { - context: Mutation["sqlContextSetDefaults"]; -}; + +export type SqlContextSetDefaultsMutation = { context: Mutation['sqlContextSetDefaults'] }; export type SqlResultCloseMutationVariables = { - connectionId: Scalars["ID"]; - contextId: Scalars["ID"]; - resultId: Scalars["ID"]; + connectionId: Scalars['ID']; + contextId: Scalars['ID']; + resultId: Scalars['ID']; }; -export type SqlResultCloseMutation = { result: Mutation["sqlResultClose"] }; + +export type SqlResultCloseMutation = { result: Mutation['sqlResultClose'] }; export type UpdateResultsDataMutationVariables = { - connectionId: Scalars["ID"]; - contextId: Scalars["ID"]; - resultsId: Scalars["ID"]; - sourceRowValues: Array>; - values?: Maybe; + connectionId: Scalars['ID']; + contextId: Scalars['ID']; + resultsId: Scalars['ID']; + sourceRowValues: Array>; + values?: Maybe; }; -export type UpdateResultsDataMutation = { - result: Maybe< - Pick & { - results: Maybe< - Array< - Pick & { - resultSet: Maybe>; - } - > - >; - } - >; -}; + +export type UpdateResultsDataMutation = { result?: Maybe<( + Pick + & { results: Array<( + Pick + & { resultSet?: Maybe> } + )>; } + )>; }; export type OpenSessionMutationVariables = {}; -export type OpenSessionMutation = { - session: Maybe< - Pick< - SessionInfo, - "createTime" | "lastAccessTime" | "cacheExpired" | "locale" - > & { - connections: Array< - Maybe> - >; - } - >; -}; + +export type OpenSessionMutation = { session: ( + Pick + & { connections: Array> } + ); }; export type ServerConfigQueryVariables = {}; -export type ServerConfigQuery = { - serverConfig: Maybe< - Pick< - ServerConfig, - | "name" - | "version" - | "productConfiguration" - | "supportsPredefinedConnections" - | "supportsProvidedConnections" - | "supportsCustomConnections" - | "supportsConnectionBrowser" - | "supportsWorkspaces" - > & { - supportedLanguages: Array< - Maybe> - >; - } - >; -}; + +export type ServerConfigQuery = { serverConfig: ( + Pick + & { supportedLanguages: Array> } + ); }; export type SessionPermissionsQueryVariables = {}; -export type SessionPermissionsQuery = { - permissions: Query["sessionPermissions"]; -}; + +export type SessionPermissionsQuery = { permissions: Query['sessionPermissions'] }; export type SessionStateQueryVariables = {}; -export type SessionStateQuery = { - sessionState: Maybe< - Pick< - SessionInfo, - "createTime" | "lastAccessTime" | "locale" | "cacheExpired" - > & { - connections: Array< - Maybe> - >; - } - >; -}; + +export type SessionStateQuery = { sessionState: ( + Pick + & { connections: Array> } + ); }; export type TouchSessionMutationVariables = {}; -export type TouchSessionMutation = Pick; -export const NavGetStructContainersDocument = gql` - query navGetStructContainers($connectionId: ID!, $catalogId: ID) { - navGetStructContainers(connectionId: $connectionId, catalog: $catalogId) { - catalogList { - name - description - type - features - } - schemaList { - name - description - type - features - } - } - } -`; -export const CloseConnectionDocument = gql` - mutation closeConnection($id: ID!) { - closeConnection(id: $id) - } -`; -export const ConnectionStateDocument = gql` - query connectionState($id: ID!) { - connection: connectionState(id: $id) { - id +export type TouchSessionMutation = Pick; + + +export const NavGetStructContainersDocument = ` + query navGetStructContainers($connectionId: ID!, $catalogId: ID) { + navGetStructContainers(connectionId: $connectionId, catalog: $catalogId) { + catalogList { name - driverId - connected - } - } -`; -export const CreateConnectionDocument = gql` - mutation createConnection($config: ConnectionConfig!) { - createConnection(config: $config) { - id - name - driverId - connected - } - } -`; -export const DataSourceListDocument = gql` - query dataSourceList { - dataSourceList { - id - name - driverId description + type + features } - } -`; -export const DriverListDocument = gql` - query driverList { - driverList { - id + schemaList { name - icon description - defaultPort - sampleURL - embedded - anonymousAccess - promotedScore + type + features } } -`; -export const DriverPropertiesDocument = gql` - query driverProperties($driverId: ID!) { - driver: driverList(id: $driverId) { - driverProperties { +} + `; +export const CloseConnectionDocument = ` + mutation closeConnection($id: ID!) { + closeConnection(id: $id) +} + `; +export const ConnectionStateDocument = ` + query connectionState($id: ID!) { + connection: connectionState(id: $id) { + id + name + driverId + connected + } +} + `; +export const CreateConnectionDocument = ` + mutation createConnection($config: ConnectionConfig!) { + createConnection(config: $config) { + id + name + driverId + connected + } +} + `; +export const DataSourceListDocument = ` + query dataSourceList { + dataSourceList { + id + name + driverId + description + } +} + `; +export const DriverListDocument = ` + query driverList { + driverList { + id + name + icon + description + defaultPort + sampleURL + embedded + anonymousAccess + promotedScore + } +} + `; +export const DriverPropertiesDocument = ` + query driverProperties($driverId: ID!) { + driver: driverList(id: $driverId) { + driverProperties { + id + displayName + description + category + dataType + defaultValue + validValues + } + driverParameters + } +} + `; +export const GetDriverByIdDocument = ` + query getDriverById($driverId: ID!) { + driverList(id: $driverId) { + id + name + icon + } +} + `; +export const OpenConnectionDocument = ` + mutation openConnection($config: ConnectionConfig!) { + openConnection(config: $config) { + id + name + driverId + connected + } +} + `; +export const TestConnectionDocument = ` + mutation testConnection($config: ConnectionConfig!) { + testConnection(config: $config) { + id + } +} + `; +export const NavNodeChildrenDocument = ` + query navNodeChildren($parentPath: ID!) { + navNodeChildren(parentPath: $parentPath) { + id + name + hasChildren + nodeType + icon + folder + inline + navigable + features + object { + features + } + } +} + `; +export const NavNodeInfoDocument = ` + query navNodeInfo($nodePath: ID!) { + navNodeInfo(nodePath: $nodePath) { + id + name + hasChildren + nodeType + icon + folder + inline + navigable + features + object { + features + } + } +} + `; +export const QueryChildrenDatabaseObjectInfoDocument = ` + query queryChildrenDatabaseObjectInfo($nodePath: ID!, $filter: ObjectPropertyFilter) { + childrenDatabaseObjectInfo: navNodeChildren(parentPath: $nodePath) { + id + object { + features + properties(filter: $filter) { id - displayName - description category dataType - defaultValue - validValues + description + displayName + features + value } - driverParameters } } -`; -export const GetDriverByIdDocument = gql` - query getDriverById($driverId: ID!) { - driverList(id: $driverId) { - id - name - icon - } - } -`; -export const OpenConnectionDocument = gql` - mutation openConnection($config: ConnectionConfig!) { - openConnection(config: $config) { - id - name - driverId - connected - } - } -`; -export const TestConnectionDocument = gql` - mutation testConnection($config: ConnectionConfig!) { - testConnection(config: $config) { - id - } - } -`; -export const NavNodeChildrenDocument = gql` - query navNodeChildren($parentPath: ID!) { - navNodeChildren(parentPath: $parentPath) { - id - name - hasChildren - nodeType - icon - folder - inline - navigable +} + `; +export const QueryDatabaseObjectInfoDocument = ` + query queryDatabaseObjectInfo($nodeId: ID!, $filter: ObjectPropertyFilter) { + objectInfo: navNodeInfo(nodePath: $nodeId) { + id + object { features - object { + properties(filter: $filter) { + id + category + dataType + description + displayName features + value } } } -`; -export const NavNodeInfoDocument = gql` - query navNodeInfo($nodePath: ID!) { - navNodeInfo(nodePath: $nodePath) { +} + `; +export const ReadSessionLogDocument = ` + query readSessionLog($maxEntries: Int!, $clearEntries: Boolean!) { + log: readSessionLog(maxEntries: $maxEntries, clearEntries: $clearEntries) { + time + type + message + stackTrace + } +} + `; +export const ChangeSessionLanguageDocument = ` + mutation changeSessionLanguage($locale: String!) { + changeSessionLanguage(locale: $locale) +} + `; +export const AuthLoginDocument = ` + query authLogin($provider: ID!, $credentials: Object!) { + user: authLogin(provider: $provider, credentials: $credentials) { + userId + displayName + authProvider + loginTime + message + } +} + `; +export const AuthLogoutDocument = ` + query authLogout { + authLogout +} + `; +export const GetAuthProvidersDocument = ` + query getAuthProviders { + providers: authProviders { + id + label + icon + description + defaultProvider + credentialParameters { id - name - hasChildren - nodeType - icon - folder - inline - navigable - features - object { - features - } + displayName + description + admin + user + possibleValues + encryption } } -`; -export const QueryChildrenDatabaseObjectInfoDocument = gql` - query queryChildrenDatabaseObjectInfo( - $nodePath: ID! - $filter: ObjectPropertyFilter - ) { - childrenDatabaseObjectInfo: navNodeChildren(parentPath: $nodePath) { - id - object { - features - properties(filter: $filter) { - id - category - dataType - description - displayName - features - value - } - } - } +} + `; +export const GetSessionUserDocument = ` + query getSessionUser { + user: sessionUser { + userId + displayName + authProvider + loginTime + message } -`; -export const QueryDatabaseObjectInfoDocument = gql` - query queryDatabaseObjectInfo($nodeId: ID!, $filter: ObjectPropertyFilter) { - objectInfo: navNodeInfo(nodePath: $nodeId) { - id - object { - features - properties(filter: $filter) { - id - category - dataType - description - displayName - features - value - } - } - } - } -`; -export const ReadSessionLogDocument = gql` - query readSessionLog($maxEntries: Int!, $clearEntries: Boolean!) { - log: readSessionLog(maxEntries: $maxEntries, clearEntries: $clearEntries) { - time - type +} + `; +export const AsyncExportTaskStatusDocument = ` + mutation asyncExportTaskStatus($taskId: String!) { + taskInfo: asyncTaskStatus(id: $taskId) { + id + name + running + status + error { message + errorCode + stackTrace + } + taskResult + } +} + `; +export const ExportDataFromContainerDocument = ` + query exportDataFromContainer($connectionId: ID!, $containerNodePath: ID!, $parameters: DataTransferParameters!) { + taskInfo: dataTransferExportDataFromContainer(connectionId: $connectionId, containerNodePath: $containerNodePath, parameters: $parameters) { + id + running + taskResult + error { + message + errorCode stackTrace } } -`; -export const ChangeSessionLanguageDocument = gql` - mutation changeSessionLanguage($locale: String!) { - changeSessionLanguage(locale: $locale) - } -`; -export const AsyncExportTaskStatusDocument = gql` - mutation asyncExportTaskStatus($taskId: String!) { - taskInfo: asyncTaskStatus(id: $taskId) { - id - name - running - status - error { - message - errorCode - stackTrace - } - taskResult +} + `; +export const ExportDataFromResultsDocument = ` + query exportDataFromResults($connectionId: ID!, $contextId: ID!, $resultsId: ID!, $parameters: DataTransferParameters!) { + taskInfo: dataTransferExportDataFromResults(connectionId: $connectionId, contextId: $contextId, resultsId: $resultsId, parameters: $parameters) { + id + running + taskResult + error { + message + errorCode + stackTrace } } -`; -export const ExportDataFromContainerDocument = gql` - query exportDataFromContainer( - $connectionId: ID! - $containerNodePath: ID! - $parameters: DataTransferParameters! - ) { - taskInfo: dataTransferExportDataFromContainer( - connectionId: $connectionId - containerNodePath: $containerNodePath - parameters: $parameters - ) { +} + `; +export const GetDataTransferProcessorsDocument = ` + query getDataTransferProcessors { + processors: dataTransferAvailableStreamProcessors { + id + name + description + fileExtension + appFileExtension + appName + order + icon + properties { id - running - taskResult - error { - message - errorCode - stackTrace - } - } - } -`; -export const ExportDataFromResultsDocument = gql` - query exportDataFromResults( - $connectionId: ID! - $contextId: ID! - $resultsId: ID! - $parameters: DataTransferParameters! - ) { - taskInfo: dataTransferExportDataFromResults( - connectionId: $connectionId - contextId: $contextId - resultsId: $resultsId - parameters: $parameters - ) { - id - running - taskResult - error { - message - errorCode - stackTrace - } - } - } -`; -export const GetDataTransferProcessorsDocument = gql` - query getDataTransferProcessors { - processors: dataTransferAvailableStreamProcessors { - id - name + displayName description - fileExtension - appFileExtension - appName - order - icon - properties { - id - displayName - description - category - dataType - defaultValue - validValues - features - } - isBinary - isHTML + category + dataType + defaultValue + validValues + features } + isBinary + isHTML } -`; -export const RemoveDataTransferFileDocument = gql` - query removeDataTransferFile($dataFileId: String!) { - result: dataTransferRemoveDataFile(dataFileId: $dataFileId) - } -`; -export const AsyncSqlExecuteQueryDocument = gql` - mutation asyncSqlExecuteQuery( - $connectionId: ID! - $contextId: ID! - $query: String! - $filter: SQLDataFilter - ) { - taskInfo: asyncSqlExecuteQuery( - connectionId: $connectionId - contextId: $contextId - sql: $query - filter: $filter - ) { - id - running - result { - duration - statusMessage - results { - updateRowCount - sourceQuery - title - resultSet { - id - columns { - dataKind - entityName - fullTypeName - icon - label - maxLength - name - position - precision - scale - typeName - } - rows - } - } - } - error { - message - errorCode - stackTrace - } - } - } -`; -export const AsyncTaskCancelDocument = gql` - mutation asyncTaskCancel($taskId: String!) { - result: asyncTaskCancel(id: $taskId) - } -`; -export const AsyncTaskStatusDocument = gql` - mutation asyncTaskStatus($taskId: String!) { - taskInfo: asyncTaskStatus(id: $taskId) { - id - running - result { - duration - statusMessage - results { - updateRowCount - sourceQuery - title - resultSet { - id - columns { - dataKind - entityName - fullTypeName - icon - label - maxLength - name - position - precision - scale - typeName - } - rows - } - } - } - error { - message - errorCode - stackTrace - } - } - } -`; -export const ExecuteSqlQueryDocument = gql` - mutation executeSqlQuery( - $connectionId: ID! - $contextId: ID! - $query: String! - $filter: SQLDataFilter - ) { - result: sqlExecuteQuery( - connectionId: $connectionId - contextId: $contextId - sql: $query - filter: $filter - ) { +} + `; +export const RemoveDataTransferFileDocument = ` + query removeDataTransferFile($dataFileId: String!) { + result: dataTransferRemoveDataFile(dataFileId: $dataFileId) +} + `; +export const AsyncSqlExecuteQueryDocument = ` + mutation asyncSqlExecuteQuery($connectionId: ID!, $contextId: ID!, $query: String!, $filter: SQLDataFilter) { + taskInfo: asyncSqlExecuteQuery(connectionId: $connectionId, contextId: $contextId, sql: $query, filter: $filter) { + id + running + result { duration statusMessage results { @@ -1737,69 +1352,25 @@ export const ExecuteSqlQueryDocument = gql` } } } - } -`; -export const MetadataGetNodeDdlDocument = gql` - query metadataGetNodeDDL($nodeId: ID!) { - metadataGetNodeDDL(nodeId: $nodeId) - } -`; -export const QuerySqlCompletionProposalsDocument = gql` - query querySqlCompletionProposals( - $connectionId: ID! - $contextId: ID! - $position: Int! - $query: String! - $maxResults: Int - ) { - sqlCompletionProposals( - connectionId: $connectionId - contextId: $contextId - maxResults: $maxResults - position: $position - query: $query - ) { - cursorPosition - displayString - icon - nodePath - replacementLength - replacementOffset - replacementString - score - type + error { + message + errorCode + stackTrace } } -`; -export const QuerySqlDialectInfoDocument = gql` - query querySqlDialectInfo($connectionId: ID!) { - dialect: sqlDialectInfo(connectionId: $connectionId) { - name - dataTypes - functions - reservedWords - quoteStrings - singleLineComments - multiLineComments - catalogSeparator - structSeparator - scriptDelimiter - } - } -`; -export const ReadDataFromContainerDocument = gql` - mutation readDataFromContainer( - $connectionId: ID! - $contextId: ID! - $containerNodePath: ID! - $filter: SQLDataFilter - ) { - readDataFromContainer( - connectionId: $connectionId - contextId: $contextId - containerNodePath: $containerNodePath - filter: $filter - ) { +} + `; +export const AsyncTaskCancelDocument = ` + mutation asyncTaskCancel($taskId: String!) { + result: asyncTaskCancel(id: $taskId) +} + `; +export const AsyncTaskStatusDocument = ` + mutation asyncTaskStatus($taskId: String!) { + taskInfo: asyncTaskStatus(id: $taskId) { + id + running + result { duration statusMessage results { @@ -1825,453 +1396,348 @@ export const ReadDataFromContainerDocument = gql` } } } - } -`; -export const SqlContextCreateDocument = gql` - mutation sqlContextCreate( - $connectionId: ID! - $defaultCatalog: String - $defaultSchema: String - ) { - context: sqlContextCreate( - connectionId: $connectionId - defaultCatalog: $defaultCatalog - defaultSchema: $defaultSchema - ) { - id - defaultCatalog - defaultSchema + error { + message + errorCode + stackTrace } } -`; -export const SqlContextDestroyDocument = gql` - mutation sqlContextDestroy($connectionId: ID!, $contextId: ID!) { - sqlContextDestroy(connectionId: $connectionId, contextId: $contextId) - } -`; -export const SqlContextSetDefaultsDocument = gql` - mutation sqlContextSetDefaults( - $connectionId: ID! - $contextId: ID! - $defaultCatalog: ID - $defaultSchema: ID - ) { - context: sqlContextSetDefaults( - connectionId: $connectionId - contextId: $contextId - defaultCatalog: $defaultCatalog - defaultSchema: $defaultSchema - ) - } -`; -export const SqlResultCloseDocument = gql` - mutation sqlResultClose($connectionId: ID!, $contextId: ID!, $resultId: ID!) { - result: sqlResultClose( - connectionId: $connectionId - contextId: $contextId - resultId: $resultId - ) - } -`; -export const UpdateResultsDataDocument = gql` - mutation updateResultsData( - $connectionId: ID! - $contextId: ID! - $resultsId: ID! - $sourceRowValues: [Object]! - $values: Object - ) { - result: updateResultsData( - connectionId: $connectionId - contextId: $contextId - resultsId: $resultsId - updateRow: $sourceRowValues - updateValues: $values - ) { - duration - results { - updateRowCount - resultSet { - id - rows +} + `; +export const ExecuteSqlQueryDocument = ` + mutation executeSqlQuery($connectionId: ID!, $contextId: ID!, $query: String!, $filter: SQLDataFilter) { + result: sqlExecuteQuery(connectionId: $connectionId, contextId: $contextId, sql: $query, filter: $filter) { + duration + statusMessage + results { + updateRowCount + sourceQuery + title + resultSet { + id + columns { + dataKind + entityName + fullTypeName + icon + label + maxLength + name + position + precision + scale + typeName } + rows } } } -`; -export const OpenSessionDocument = gql` - mutation openSession { - session: openSession { - createTime - lastAccessTime - cacheExpired - locale - connections { +} + `; +export const MetadataGetNodeDdlDocument = ` + query metadataGetNodeDDL($nodeId: ID!) { + metadataGetNodeDDL(nodeId: $nodeId) +} + `; +export const QuerySqlCompletionProposalsDocument = ` + query querySqlCompletionProposals($connectionId: ID!, $contextId: ID!, $position: Int!, $query: String!, $maxResults: Int) { + sqlCompletionProposals(connectionId: $connectionId, contextId: $contextId, maxResults: $maxResults, position: $position, query: $query) { + cursorPosition + displayString + icon + nodePath + replacementLength + replacementOffset + replacementString + score + type + } +} + `; +export const QuerySqlDialectInfoDocument = ` + query querySqlDialectInfo($connectionId: ID!) { + dialect: sqlDialectInfo(connectionId: $connectionId) { + name + dataTypes + functions + reservedWords + quoteStrings + singleLineComments + multiLineComments + catalogSeparator + structSeparator + scriptDelimiter + } +} + `; +export const ReadDataFromContainerDocument = ` + mutation readDataFromContainer($connectionId: ID!, $contextId: ID!, $containerNodePath: ID!, $filter: SQLDataFilter) { + readDataFromContainer(connectionId: $connectionId, contextId: $contextId, containerNodePath: $containerNodePath, filter: $filter) { + duration + statusMessage + results { + updateRowCount + sourceQuery + title + resultSet { id - name - driverId - connected + columns { + dataKind + entityName + fullTypeName + icon + label + maxLength + name + position + precision + scale + typeName + } + rows } } } -`; -export const ServerConfigDocument = gql` - query serverConfig { - serverConfig { +} + `; +export const SqlContextCreateDocument = ` + mutation sqlContextCreate($connectionId: ID!, $defaultCatalog: String, $defaultSchema: String) { + context: sqlContextCreate(connectionId: $connectionId, defaultCatalog: $defaultCatalog, defaultSchema: $defaultSchema) { + id + defaultCatalog + defaultSchema + } +} + `; +export const SqlContextDestroyDocument = ` + mutation sqlContextDestroy($connectionId: ID!, $contextId: ID!) { + sqlContextDestroy(connectionId: $connectionId, contextId: $contextId) +} + `; +export const SqlContextSetDefaultsDocument = ` + mutation sqlContextSetDefaults($connectionId: ID!, $contextId: ID!, $defaultCatalog: ID, $defaultSchema: ID) { + context: sqlContextSetDefaults(connectionId: $connectionId, contextId: $contextId, defaultCatalog: $defaultCatalog, defaultSchema: $defaultSchema) +} + `; +export const SqlResultCloseDocument = ` + mutation sqlResultClose($connectionId: ID!, $contextId: ID!, $resultId: ID!) { + result: sqlResultClose(connectionId: $connectionId, contextId: $contextId, resultId: $resultId) +} + `; +export const UpdateResultsDataDocument = ` + mutation updateResultsData($connectionId: ID!, $contextId: ID!, $resultsId: ID!, $sourceRowValues: [Object]!, $values: Object) { + result: updateResultsData(connectionId: $connectionId, contextId: $contextId, resultsId: $resultsId, updateRow: $sourceRowValues, updateValues: $values) { + duration + results { + updateRowCount + resultSet { + id + rows + } + } + } +} + `; +export const OpenSessionDocument = ` + mutation openSession { + session: openSession { + createTime + lastAccessTime + cacheExpired + locale + connections { + id name - version - productConfiguration - supportsPredefinedConnections - supportsProvidedConnections - supportsCustomConnections - supportsConnectionBrowser - supportsWorkspaces - supportedLanguages { - isoCode - displayName - nativeName - } - productConfiguration + driverId + connected } } -`; -export const SessionPermissionsDocument = gql` - query sessionPermissions { - permissions: sessionPermissions +} + `; +export const ServerConfigDocument = ` + query serverConfig { + serverConfig { + name + version + productConfiguration + supportsPredefinedConnections + supportsProvidedConnections + supportsCustomConnections + supportsConnectionBrowser + supportsWorkspaces + anonymousAccessEnabled + authenticationEnabled + supportedLanguages { + isoCode + displayName + nativeName + } + productConfiguration } -`; -export const SessionStateDocument = gql` - query sessionState { - sessionState { - createTime - lastAccessTime - locale - cacheExpired - connections { - id - name - driverId - connected - } +} + `; +export const SessionPermissionsDocument = ` + query sessionPermissions { + permissions: sessionPermissions +} + `; +export const SessionStateDocument = ` + query sessionState { + sessionState { + createTime + lastAccessTime + locale + cacheExpired + connections { + id + name + driverId + connected } } -`; -export const TouchSessionDocument = gql` - mutation touchSession { - touchSession - } -`; -export function getSdk(client: GraphQLClient) { +} + `; +export const TouchSessionDocument = ` + mutation touchSession { + touchSession +} + `; + +export type SdkFunctionWrapper = (action: () => Promise) => Promise; + + +const defaultWrapper: SdkFunctionWrapper = sdkFunction => sdkFunction(); +export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper = defaultWrapper) { return { - navGetStructContainers( - variables: NavGetStructContainersQueryVariables, - ): Promise { - return client.request( - print(NavGetStructContainersDocument), - variables, - ); + navGetStructContainers(variables: NavGetStructContainersQueryVariables): Promise { + return withWrapper(() => client.request(NavGetStructContainersDocument, variables)); }, - closeConnection( - variables: CloseConnectionMutationVariables, - ): Promise { - return client.request( - print(CloseConnectionDocument), - variables, - ); + closeConnection(variables: CloseConnectionMutationVariables): Promise { + return withWrapper(() => client.request(CloseConnectionDocument, variables)); }, - connectionState( - variables: ConnectionStateQueryVariables, - ): Promise { - return client.request( - print(ConnectionStateDocument), - variables, - ); + connectionState(variables: ConnectionStateQueryVariables): Promise { + return withWrapper(() => client.request(ConnectionStateDocument, variables)); }, - createConnection( - variables: CreateConnectionMutationVariables, - ): Promise { - return client.request( - print(CreateConnectionDocument), - variables, - ); + createConnection(variables: CreateConnectionMutationVariables): Promise { + return withWrapper(() => client.request(CreateConnectionDocument, variables)); }, - dataSourceList( - variables?: DataSourceListQueryVariables, - ): Promise { - return client.request( - print(DataSourceListDocument), - variables, - ); + dataSourceList(variables?: DataSourceListQueryVariables): Promise { + return withWrapper(() => client.request(DataSourceListDocument, variables)); }, driverList(variables?: DriverListQueryVariables): Promise { - return client.request( - print(DriverListDocument), - variables, - ); + return withWrapper(() => client.request(DriverListDocument, variables)); }, - driverProperties( - variables: DriverPropertiesQueryVariables, - ): Promise { - return client.request( - print(DriverPropertiesDocument), - variables, - ); + driverProperties(variables: DriverPropertiesQueryVariables): Promise { + return withWrapper(() => client.request(DriverPropertiesDocument, variables)); }, - getDriverById( - variables: GetDriverByIdQueryVariables, - ): Promise { - return client.request( - print(GetDriverByIdDocument), - variables, - ); + getDriverById(variables: GetDriverByIdQueryVariables): Promise { + return withWrapper(() => client.request(GetDriverByIdDocument, variables)); }, - openConnection( - variables: OpenConnectionMutationVariables, - ): Promise { - return client.request( - print(OpenConnectionDocument), - variables, - ); + openConnection(variables: OpenConnectionMutationVariables): Promise { + return withWrapper(() => client.request(OpenConnectionDocument, variables)); }, - testConnection( - variables: TestConnectionMutationVariables, - ): Promise { - return client.request( - print(TestConnectionDocument), - variables, - ); + testConnection(variables: TestConnectionMutationVariables): Promise { + return withWrapper(() => client.request(TestConnectionDocument, variables)); }, - navNodeChildren( - variables: NavNodeChildrenQueryVariables, - ): Promise { - return client.request( - print(NavNodeChildrenDocument), - variables, - ); + navNodeChildren(variables: NavNodeChildrenQueryVariables): Promise { + return withWrapper(() => client.request(NavNodeChildrenDocument, variables)); }, - navNodeInfo( - variables: NavNodeInfoQueryVariables, - ): Promise { - return client.request( - print(NavNodeInfoDocument), - variables, - ); + navNodeInfo(variables: NavNodeInfoQueryVariables): Promise { + return withWrapper(() => client.request(NavNodeInfoDocument, variables)); }, - queryChildrenDatabaseObjectInfo( - variables: QueryChildrenDatabaseObjectInfoQueryVariables, - ): Promise { - return client.request( - print(QueryChildrenDatabaseObjectInfoDocument), - variables, - ); + queryChildrenDatabaseObjectInfo(variables: QueryChildrenDatabaseObjectInfoQueryVariables): Promise { + return withWrapper(() => client.request(QueryChildrenDatabaseObjectInfoDocument, variables)); }, - queryDatabaseObjectInfo( - variables: QueryDatabaseObjectInfoQueryVariables, - ): Promise { - return client.request( - print(QueryDatabaseObjectInfoDocument), - variables, - ); + queryDatabaseObjectInfo(variables: QueryDatabaseObjectInfoQueryVariables): Promise { + return withWrapper(() => client.request(QueryDatabaseObjectInfoDocument, variables)); }, - readSessionLog( - variables: ReadSessionLogQueryVariables, - ): Promise { - return client.request( - print(ReadSessionLogDocument), - variables, - ); + readSessionLog(variables: ReadSessionLogQueryVariables): Promise { + return withWrapper(() => client.request(ReadSessionLogDocument, variables)); }, - changeSessionLanguage( - variables: ChangeSessionLanguageMutationVariables, - ): Promise { - return client.request( - print(ChangeSessionLanguageDocument), - variables, - ); + changeSessionLanguage(variables: ChangeSessionLanguageMutationVariables): Promise { + return withWrapper(() => client.request(ChangeSessionLanguageDocument, variables)); }, - asyncExportTaskStatus( - variables: AsyncExportTaskStatusMutationVariables, - ): Promise { - return client.request( - print(AsyncExportTaskStatusDocument), - variables, - ); + authLogin(variables: AuthLoginQueryVariables): Promise { + return withWrapper(() => client.request(AuthLoginDocument, variables)); }, - exportDataFromContainer( - variables: ExportDataFromContainerQueryVariables, - ): Promise { - return client.request( - print(ExportDataFromContainerDocument), - variables, - ); + authLogout(variables?: AuthLogoutQueryVariables): Promise { + return withWrapper(() => client.request(AuthLogoutDocument, variables)); }, - exportDataFromResults( - variables: ExportDataFromResultsQueryVariables, - ): Promise { - return client.request( - print(ExportDataFromResultsDocument), - variables, - ); + getAuthProviders(variables?: GetAuthProvidersQueryVariables): Promise { + return withWrapper(() => client.request(GetAuthProvidersDocument, variables)); }, - getDataTransferProcessors( - variables?: GetDataTransferProcessorsQueryVariables, - ): Promise { - return client.request( - print(GetDataTransferProcessorsDocument), - variables, - ); + getSessionUser(variables?: GetSessionUserQueryVariables): Promise { + return withWrapper(() => client.request(GetSessionUserDocument, variables)); }, - removeDataTransferFile( - variables: RemoveDataTransferFileQueryVariables, - ): Promise { - return client.request( - print(RemoveDataTransferFileDocument), - variables, - ); + asyncExportTaskStatus(variables: AsyncExportTaskStatusMutationVariables): Promise { + return withWrapper(() => client.request(AsyncExportTaskStatusDocument, variables)); }, - asyncSqlExecuteQuery( - variables: AsyncSqlExecuteQueryMutationVariables, - ): Promise { - return client.request( - print(AsyncSqlExecuteQueryDocument), - variables, - ); + exportDataFromContainer(variables: ExportDataFromContainerQueryVariables): Promise { + return withWrapper(() => client.request(ExportDataFromContainerDocument, variables)); }, - asyncTaskCancel( - variables: AsyncTaskCancelMutationVariables, - ): Promise { - return client.request( - print(AsyncTaskCancelDocument), - variables, - ); + exportDataFromResults(variables: ExportDataFromResultsQueryVariables): Promise { + return withWrapper(() => client.request(ExportDataFromResultsDocument, variables)); }, - asyncTaskStatus( - variables: AsyncTaskStatusMutationVariables, - ): Promise { - return client.request( - print(AsyncTaskStatusDocument), - variables, - ); + getDataTransferProcessors(variables?: GetDataTransferProcessorsQueryVariables): Promise { + return withWrapper(() => client.request(GetDataTransferProcessorsDocument, variables)); }, - executeSqlQuery( - variables: ExecuteSqlQueryMutationVariables, - ): Promise { - return client.request( - print(ExecuteSqlQueryDocument), - variables, - ); + removeDataTransferFile(variables: RemoveDataTransferFileQueryVariables): Promise { + return withWrapper(() => client.request(RemoveDataTransferFileDocument, variables)); }, - metadataGetNodeDDL( - variables: MetadataGetNodeDdlQueryVariables, - ): Promise { - return client.request( - print(MetadataGetNodeDdlDocument), - variables, - ); + asyncSqlExecuteQuery(variables: AsyncSqlExecuteQueryMutationVariables): Promise { + return withWrapper(() => client.request(AsyncSqlExecuteQueryDocument, variables)); }, - querySqlCompletionProposals( - variables: QuerySqlCompletionProposalsQueryVariables, - ): Promise { - return client.request( - print(QuerySqlCompletionProposalsDocument), - variables, - ); + asyncTaskCancel(variables: AsyncTaskCancelMutationVariables): Promise { + return withWrapper(() => client.request(AsyncTaskCancelDocument, variables)); }, - querySqlDialectInfo( - variables: QuerySqlDialectInfoQueryVariables, - ): Promise { - return client.request( - print(QuerySqlDialectInfoDocument), - variables, - ); + asyncTaskStatus(variables: AsyncTaskStatusMutationVariables): Promise { + return withWrapper(() => client.request(AsyncTaskStatusDocument, variables)); }, - readDataFromContainer( - variables: ReadDataFromContainerMutationVariables, - ): Promise { - return client.request( - print(ReadDataFromContainerDocument), - variables, - ); + executeSqlQuery(variables: ExecuteSqlQueryMutationVariables): Promise { + return withWrapper(() => client.request(ExecuteSqlQueryDocument, variables)); }, - sqlContextCreate( - variables: SqlContextCreateMutationVariables, - ): Promise { - return client.request( - print(SqlContextCreateDocument), - variables, - ); + metadataGetNodeDDL(variables: MetadataGetNodeDdlQueryVariables): Promise { + return withWrapper(() => client.request(MetadataGetNodeDdlDocument, variables)); }, - sqlContextDestroy( - variables: SqlContextDestroyMutationVariables, - ): Promise { - return client.request( - print(SqlContextDestroyDocument), - variables, - ); + querySqlCompletionProposals(variables: QuerySqlCompletionProposalsQueryVariables): Promise { + return withWrapper(() => client.request(QuerySqlCompletionProposalsDocument, variables)); }, - sqlContextSetDefaults( - variables: SqlContextSetDefaultsMutationVariables, - ): Promise { - return client.request( - print(SqlContextSetDefaultsDocument), - variables, - ); + querySqlDialectInfo(variables: QuerySqlDialectInfoQueryVariables): Promise { + return withWrapper(() => client.request(QuerySqlDialectInfoDocument, variables)); }, - sqlResultClose( - variables: SqlResultCloseMutationVariables, - ): Promise { - return client.request( - print(SqlResultCloseDocument), - variables, - ); + readDataFromContainer(variables: ReadDataFromContainerMutationVariables): Promise { + return withWrapper(() => client.request(ReadDataFromContainerDocument, variables)); }, - updateResultsData( - variables: UpdateResultsDataMutationVariables, - ): Promise { - return client.request( - print(UpdateResultsDataDocument), - variables, - ); + sqlContextCreate(variables: SqlContextCreateMutationVariables): Promise { + return withWrapper(() => client.request(SqlContextCreateDocument, variables)); }, - openSession( - variables?: OpenSessionMutationVariables, - ): Promise { - return client.request( - print(OpenSessionDocument), - variables, - ); + sqlContextDestroy(variables: SqlContextDestroyMutationVariables): Promise { + return withWrapper(() => client.request(SqlContextDestroyDocument, variables)); }, - serverConfig( - variables?: ServerConfigQueryVariables, - ): Promise { - return client.request( - print(ServerConfigDocument), - variables, - ); + sqlContextSetDefaults(variables: SqlContextSetDefaultsMutationVariables): Promise { + return withWrapper(() => client.request(SqlContextSetDefaultsDocument, variables)); }, - sessionPermissions( - variables?: SessionPermissionsQueryVariables, - ): Promise { - return client.request( - print(SessionPermissionsDocument), - variables, - ); + sqlResultClose(variables: SqlResultCloseMutationVariables): Promise { + return withWrapper(() => client.request(SqlResultCloseDocument, variables)); }, - sessionState( - variables?: SessionStateQueryVariables, - ): Promise { - return client.request( - print(SessionStateDocument), - variables, - ); + updateResultsData(variables: UpdateResultsDataMutationVariables): Promise { + return withWrapper(() => client.request(UpdateResultsDataDocument, variables)); }, - touchSession( - variables?: TouchSessionMutationVariables, - ): Promise { - return client.request( - print(TouchSessionDocument), - variables, - ); + openSession(variables?: OpenSessionMutationVariables): Promise { + return withWrapper(() => client.request(OpenSessionDocument, variables)); + }, + serverConfig(variables?: ServerConfigQueryVariables): Promise { + return withWrapper(() => client.request(ServerConfigDocument, variables)); + }, + sessionPermissions(variables?: SessionPermissionsQueryVariables): Promise { + return withWrapper(() => client.request(SessionPermissionsDocument, variables)); + }, + sessionState(variables?: SessionStateQueryVariables): Promise { + return withWrapper(() => client.request(SessionStateDocument, variables)); + }, + touchSession(variables?: TouchSessionMutationVariables): Promise { + return withWrapper(() => client.request(TouchSessionDocument, variables)); }, }; } +export type Sdk = ReturnType; diff --git a/webapp/packages/custom-connection-plugin/package.json b/webapp/packages/custom-connection-plugin/package.json index 23551f72b3..c7224ee747 100644 --- a/webapp/packages/custom-connection-plugin/package.json +++ b/webapp/packages/custom-connection-plugin/package.json @@ -17,14 +17,6 @@ }, "peerDependencies": { "@dbeaver/core": "^0.1.0", - "@material/button": "^4.0.0", - "@material/elevation": "^4.0.0", - "@material/ripple": "^4.0.0", - "@material/theme": "^4.0.0", - "@material/typography": "^4.0.0", - "graphql": "^14.5.8", - "graphql-request": "^1.8.2", - "graphql-tag": "^2.10.1", "mobx": "^5.15.4", "mobx-react": "^6.2.2", "react": "^16.13.1", diff --git a/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionForm/ConnectionForm.tsx b/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionForm/ConnectionForm.tsx index afa625d03e..d1394ae450 100644 --- a/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionForm/ConnectionForm.tsx +++ b/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionForm/ConnectionForm.tsx @@ -32,7 +32,7 @@ export const ConnectionForm = observer(function ConnectionForm({ const translate = useTranslate(); return styled(useStyles(formStyles))( - <> + )} {!driver?.anonymousAccess && } - + ); }); diff --git a/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionForm/formStyles.ts b/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionForm/formStyles.ts index ccc9a48c55..001af5123b 100644 --- a/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionForm/formStyles.ts +++ b/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionForm/formStyles.ts @@ -9,6 +9,10 @@ import { css } from 'reshadow'; export const formStyles = css` + connection-form { + flex-direction: column; + padding: 18px 24px; + } connection-type { padding: 12px; } diff --git a/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionFormDialog.tsx b/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionFormDialog.tsx index 0ef2030383..cef1038a22 100644 --- a/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionFormDialog.tsx +++ b/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionFormDialog.tsx @@ -7,13 +7,13 @@ */ import { observer } from 'mobx-react'; -import { - useTabState, Tab as BaseTab, TabList, TabPanel as BaseTabPanel, -} from 'reakit/Tab'; +import { useState } from 'react'; import styled, { css } from 'reshadow'; import { DBDriver } from '@dbeaver/core/app'; -import { SubmittingForm, ErrorMessage } from '@dbeaver/core/blocks'; +import { + SubmittingForm, ErrorMessage, TabsState, TabList, Tab, TabTitle, TabPanel +} from '@dbeaver/core/blocks'; import { useController } from '@dbeaver/core/di'; import { CommonDialogWrapper } from '@dbeaver/core/dialogs'; import { useTranslate } from '@dbeaver/core/localization'; @@ -24,10 +24,9 @@ import { ConnectionFormDialogController } from './ConnectionFormDialogController import { ConnectionFormDialogFooter } from './ConnectionFormDialogFooter'; import { DriverProperties } from './DriverProperties/DriverProperties'; - const styles = composes( css` - BaseTab { + Tab { composes: theme-ripple theme-background-secondary theme-text-on-secondary from global; } ErrorMessage { @@ -41,10 +40,8 @@ const styles = composes( box-sizing: border-box; } CommonDialogWrapper { - display: flex; - flex-direction: column; - max-height: 330px; - min-height: 330px; + max-height: 500px; + min-height: 500px; } SubmittingForm { overflow: auto; @@ -55,39 +52,20 @@ const styles = composes( flex-direction: column; } - BaseTab, BaseTabPanel { - outline: none; - } - TabList { box-sizing: border-box; display: inline-flex; width: 100%; padding-left: 24px; } - BaseTab { + Tab { composes: theme-typography--body2 from global; text-transform: uppercase; - padding: 12px 16px; - border-top: solid 2px transparent; - height: 48px; + font-weight: normal; - &:global([aria-selected='true']) { - border-top-color: #fd1d48; - - &:before { - display: none; - } + &:global([aria-selected=true]) { + font-weight: normal !important; } - - &:not(:global([aria-selected='true'])) { - cursor: pointer; - background-color: transparent !important; - } - } - BaseTabPanel::first-child { - flex-direction: column; - padding: 18px 24px; } ErrorMessage { position: sticky; @@ -113,50 +91,54 @@ export const ConnectionFormDialog = observer( }: ConnectionFormDialogProps) { const translate = useTranslate(); const controller = useController(ConnectionFormDialogController, driver, onClose); - const tab = useTabState({ - selectedId: 'options', - }); + const [loadProperties, setLoadProperties] = useState(false); return styled(useStyles(styles))( - - {translate('customConnection_options')} - {translate('customConnection_properties')} - - )} - footer={( - - )} - onReject={onClose} - > - - - - - - + + + + + setLoadProperties(true)} > + + + + )} + footer={( + - - - {controller.responseMessage && ( - - )} - + )} + onReject={onClose} + > + + + + + + + + + {controller.error.responseMessage && ( + + )} + + ); } ); diff --git a/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionFormDialogController.ts b/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionFormDialogController.ts index 26648662a4..4c04ceaed7 100644 --- a/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionFormDialogController.ts +++ b/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionFormDialogController.ts @@ -12,7 +12,7 @@ import { DBDriver, ErrorDetailsDialog } from '@dbeaver/core/app'; import { injectable, IInitializableController, IDestructibleController } from '@dbeaver/core/di'; import { CommonDialogService } from '@dbeaver/core/dialogs'; import { NotificationService } from '@dbeaver/core/eventsLog'; -import { ConnectionConfig, GQLError } from '@dbeaver/core/sdk'; +import { ConnectionConfig, GQLErrorCatcher } from '@dbeaver/core/sdk'; import { CustomConnectionService } from '../../CustomConnectionService'; @@ -39,10 +39,7 @@ implements IInitializableController, IDestructibleController { url: '', properties: {}, } - @observable hasDetails = false - @observable responseMessage: string | null = null - - private exception: GQLError | null = null; + readonly error = new GQLErrorCatcher(); private onClose!: () => void private isDistructed = false; @@ -70,7 +67,7 @@ implements IInitializableController, IDestructibleController { onTestConnection = async () => { this.isConnecting = true; - this.clearError(); + this.error.clear(); try { await this.customConnectionService.testConnectionAsync(this.getConnectionConfig()); @@ -84,7 +81,7 @@ implements IInitializableController, IDestructibleController { onCreateConnection = async () => { this.isConnecting = true; - this.clearError(); + this.error.clear(); try { const connection = await this.customConnectionService.createConnectionAsync(this.getConnectionConfig()); @@ -98,8 +95,8 @@ implements IInitializableController, IDestructibleController { } onShowDetails = () => { - if (this.exception) { - this.commonDialogService.open(ErrorDetailsDialog, this.exception); + if (this.error.exception) { + this.commonDialogService.open(ErrorDetailsDialog, this.error.exception); } } @@ -144,12 +141,6 @@ implements IInitializableController, IDestructibleController { this.config.properties = {}; } - private clearError() { - this.responseMessage = null; - this.hasDetails = false; - this.exception = null; - } - /** * Creates connection name based on connection url * @param defaultName default connection name if url parsing failed @@ -175,11 +166,7 @@ implements IInitializableController, IDestructibleController { } private showError(exception: Error, message: string) { - if (exception instanceof GQLError && !this.isDistructed) { - this.responseMessage = exception.errorText; - this.hasDetails = exception.hasDetails(); - this.exception = exception; - } else { + if (!this.error.catch(exception) || this.isDistructed) { this.notificationService.logException(exception, message); } } diff --git a/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionFormDialogFooter.tsx b/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionFormDialogFooter.tsx index 602fdba851..ce38646f26 100644 --- a/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionFormDialogFooter.tsx +++ b/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/ConnectionFormDialogFooter.tsx @@ -15,8 +15,10 @@ import { useTranslate } from '@dbeaver/core/localization'; const styles = css` controls { display: flex; + flex: 1; height: 100%; align-items: center; + margin: auto; } fill { diff --git a/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/DriverProperties/DriverProperties.tsx b/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/DriverProperties/DriverProperties.tsx index b759af3c49..db45e66712 100644 --- a/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/DriverProperties/DriverProperties.tsx +++ b/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/DriverProperties/DriverProperties.tsx @@ -22,6 +22,7 @@ const styles = css` display: flex; flex: 1; flex-direction: column; + overflow: auto; } `; @@ -32,21 +33,21 @@ type DriverPropertyState = { type DriverPropertiesProps = { driver: DBDriver; state: DriverPropertyState; - isSelected: boolean; + loadProperties: boolean; } export const DriverProperties = observer(function DriverProperties({ driver, state, - isSelected, + loadProperties, }: DriverPropertiesProps) { const controller = useController(DriverPropertiesController, driver); useEffect(() => { - if (isSelected) { + if (loadProperties) { controller.loadDriverProperties(); } - }, [isSelected]); + }, [loadProperties]); return styled(useStyles(styles))( diff --git a/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/DriverProperties/DriverPropertiesController.ts b/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/DriverProperties/DriverPropertiesController.ts index b0a268c68d..0cec52f6c1 100644 --- a/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/DriverProperties/DriverPropertiesController.ts +++ b/webapp/packages/custom-connection-plugin/src/CustomConnection/ConnectionFormDialog/DriverProperties/DriverPropertiesController.ts @@ -47,7 +47,7 @@ export class DriverPropertiesController implements IInitializableController { onAddProperty = () => { this.driverProperties.unshift({ id: uuid(), - name: 'property', + key: 'property', defaultValue: '', }); } @@ -60,8 +60,9 @@ export class DriverPropertiesController implements IInitializableController { try { const driverProperties = await this.customConnectionService.loadDriverProperties(this.driver.id); this.driverProperties = observable(driverProperties.map(property => ({ - id: property.displayName!, - name: property.displayName!, + id: property.id!, + key: property.id!, + displayName: property.displayName!, defaultValue: property.defaultValue, description: property.description, validValues: property.validValues, diff --git a/webapp/packages/custom-connection-plugin/src/CustomConnection/CustomConnectionDialog.tsx b/webapp/packages/custom-connection-plugin/src/CustomConnection/CustomConnectionDialog.tsx index 8df2c23d58..b0d07b4909 100644 --- a/webapp/packages/custom-connection-plugin/src/CustomConnection/CustomConnectionDialog.tsx +++ b/webapp/packages/custom-connection-plugin/src/CustomConnection/CustomConnectionDialog.tsx @@ -9,7 +9,7 @@ import { observer } from 'mobx-react'; import { useController } from '@dbeaver/core/di'; -import { DialogComponent, DialogComponentProps } from '@dbeaver/core/dialogs'; +import { DialogComponent } from '@dbeaver/core/dialogs'; import { useTranslate } from '@dbeaver/core/localization'; import { ConnectionFormDialog } from './ConnectionFormDialog/ConnectionFormDialog'; @@ -17,7 +17,7 @@ import { CustomConnectionController, ConnectionStep } from './CustomConnectionCo import { DriverSelectorDialog } from './DriverSelectorDialog/DriverSelectorDialog'; export const CustomConnectionDialog: DialogComponent = observer( - function CustomConnectionDialog(props: DialogComponentProps) { + function CustomConnectionDialog(props) { const controller = useController(CustomConnectionController); let title = useTranslate('basicConnection_connectionDialog_newConnection'); diff --git a/webapp/packages/custom-connection-plugin/src/CustomConnection/DriverSelectorDialog/DriverSelectorDialog.tsx b/webapp/packages/custom-connection-plugin/src/CustomConnection/DriverSelectorDialog/DriverSelectorDialog.tsx index 4c4dd7c4e4..89bbdb6c44 100644 --- a/webapp/packages/custom-connection-plugin/src/CustomConnection/DriverSelectorDialog/DriverSelectorDialog.tsx +++ b/webapp/packages/custom-connection-plugin/src/CustomConnection/DriverSelectorDialog/DriverSelectorDialog.tsx @@ -17,8 +17,8 @@ import { DriverSelector } from './DriverSelector'; const styles = css` CommonDialogWrapper { - display: flex; - min-height: 330px; + max-height: 500px; + min-height: 500px; } DriverSelector { flex: 1; diff --git a/webapp/packages/data-export-plugin/src/Dialog/DataExportController.ts b/webapp/packages/data-export-plugin/src/Dialog/DataExportController.ts index 9c3479c46d..9773dabce2 100644 --- a/webapp/packages/data-export-plugin/src/Dialog/DataExportController.ts +++ b/webapp/packages/data-export-plugin/src/Dialog/DataExportController.ts @@ -89,8 +89,9 @@ export class DataExportController implements IInitializableController { .get(processorId)!; this.properties = this.processor.properties?.map(property => ({ - id: property.displayName!, - name: property.displayName!, + id: property.id!, + key: property.id!, + displayName: property.displayName!, description: property.description, validValues: property.validValues, defaultValue: property.defaultValue, diff --git a/webapp/packages/data-export-plugin/src/Dialog/ProcessorConfigureDialog.tsx b/webapp/packages/data-export-plugin/src/Dialog/ProcessorConfigureDialog.tsx index 1e8ea520cf..847d35efd2 100644 --- a/webapp/packages/data-export-plugin/src/Dialog/ProcessorConfigureDialog.tsx +++ b/webapp/packages/data-export-plugin/src/Dialog/ProcessorConfigureDialog.tsx @@ -18,8 +18,8 @@ import { ProcessorConfigureDialogFooter } from './ProcessorConfigureDialogFooter const styles = css` CommonDialogWrapper { - display: flex; - min-height: 330px; + max-height: 500px; + min-height: 500px; } PropertiesTable { flex: 1; diff --git a/webapp/packages/data-export-plugin/src/Dialog/ProcessorConfigureDialogFooter.tsx b/webapp/packages/data-export-plugin/src/Dialog/ProcessorConfigureDialogFooter.tsx index 70f46e4482..5ca6479c0b 100644 --- a/webapp/packages/data-export-plugin/src/Dialog/ProcessorConfigureDialogFooter.tsx +++ b/webapp/packages/data-export-plugin/src/Dialog/ProcessorConfigureDialogFooter.tsx @@ -15,8 +15,10 @@ import { useTranslate } from '@dbeaver/core/localization'; const styles = css` controls { display: flex; + flex: 1; height: 100%; align-items: center; + margin: auto; } fill { diff --git a/webapp/packages/data-export-plugin/src/Dialog/ProcessorSelectDialog.tsx b/webapp/packages/data-export-plugin/src/Dialog/ProcessorSelectDialog.tsx index 9004a37226..fb11495de4 100644 --- a/webapp/packages/data-export-plugin/src/Dialog/ProcessorSelectDialog.tsx +++ b/webapp/packages/data-export-plugin/src/Dialog/ProcessorSelectDialog.tsx @@ -20,9 +20,8 @@ import { ExportProcessorList } from './ExportProcessorList/ExportProcessorList'; const styles = css` CommonDialogWrapper { - display: flex; - flex-direction: column; - min-height: 330px; + max-height: 500px; + min-height: 500px; } ExportProcessorList { flex: 1; diff --git a/webapp/packages/data-viewer-plugin/package.json b/webapp/packages/data-viewer-plugin/package.json index 116413ad27..37690b8d79 100644 --- a/webapp/packages/data-viewer-plugin/package.json +++ b/webapp/packages/data-viewer-plugin/package.json @@ -20,14 +20,6 @@ "@dbeaver/ag-grid-plugin": "^0.1.0", "@dbeaver/object-viewer-plugin": "^0.1.0", "@dbeaver/core": "^0.1.0", - "@material/button": "^4.0.0", - "@material/elevation": "^4.0.0", - "@material/ripple": "^4.0.0", - "@material/theme": "^4.0.0", - "@material/typography": "^4.0.0", - "graphql": "^14.5.8", - "graphql-request": "^1.8.2", - "graphql-tag": "^2.10.1", "mobx": "^5.15.4", "mobx-react": "^6.2.2", "react": "^16.13.1", diff --git a/webapp/packages/data-viewer-plugin/src/TableViewer/ErrorDialog.tsx b/webapp/packages/data-viewer-plugin/src/TableViewer/ErrorDialog.tsx index 956141c414..d4fed0c349 100644 --- a/webapp/packages/data-viewer-plugin/src/TableViewer/ErrorDialog.tsx +++ b/webapp/packages/data-viewer-plugin/src/TableViewer/ErrorDialog.tsx @@ -18,8 +18,10 @@ import { useStyles } from '@dbeaver/core/theming'; const styles = css` controls { display: flex; + flex: 1; height: 100%; align-items: center; + margin: auto; } fill { diff --git a/webapp/packages/dbeaver/package.json b/webapp/packages/dbeaver/package.json index 5ce0d8f780..be1d7fe0d5 100644 --- a/webapp/packages/dbeaver/package.json +++ b/webapp/packages/dbeaver/package.json @@ -16,11 +16,11 @@ "gql:gen": "graphql-codegen", "gql:gen:dev": "graphql-codegen --watch", "lint": "eslint ./src/ --ext .ts,.tsx", - "lint-fix": "eslint ./src/ --ext .ts,.tsx --fix", - "prettify": "prettier --write **/*.ts **/*.tsx" + "lint-fix": "eslint ./src/ --ext .ts,.tsx --fix" }, "dependencies": { "@dbeaver/administration": "^0.1.0", + "@dbeaver/authentication": "^0.1.0", "@dbeaver/ag-grid-plugin": "^0.1.0", "@dbeaver/basic-connection-plugin": "^0.1.0", "@dbeaver/builder": "^0.1.0", diff --git a/webapp/packages/dbeaver/plugins-list.js b/webapp/packages/dbeaver/plugins-list.js index bd0cc9065d..19d2e27b30 100644 --- a/webapp/packages/dbeaver/plugins-list.js +++ b/webapp/packages/dbeaver/plugins-list.js @@ -4,6 +4,7 @@ module.exports = [ "@dbeaver/administration", + "@dbeaver/authentication", "@dbeaver/ag-grid-plugin", "@dbeaver/basic-connection-plugin", "@dbeaver/custom-connection-plugin", diff --git a/webapp/packages/dbeaver/public/icons/icons.svg b/webapp/packages/dbeaver/public/icons/icons.svg index 388d366b24..14c8e85c23 100644 --- a/webapp/packages/dbeaver/public/icons/icons.svg +++ b/webapp/packages/dbeaver/public/icons/icons.svg @@ -49,6 +49,14 @@ + + + + + + + + diff --git a/webapp/packages/ddl-viewer-plugin/package.json b/webapp/packages/ddl-viewer-plugin/package.json index af7f0d14b8..4a9bcc272d 100644 --- a/webapp/packages/ddl-viewer-plugin/package.json +++ b/webapp/packages/ddl-viewer-plugin/package.json @@ -20,14 +20,6 @@ "@dbeaver/core": "^0.1.0", "@dbeaver/object-viewer-plugin": "^0.1.0", "@dbeaver/sql-editor": "^0.1.0", - "@material/button": "^4.0.0", - "@material/elevation": "^4.0.0", - "@material/ripple": "^4.0.0", - "@material/theme": "^4.0.0", - "@material/typography": "^4.0.0", - "graphql": "^14.5.8", - "graphql-request": "^1.8.2", - "graphql-tag": "^2.10.1", "mobx": "^5.15.4", "mobx-react": "^6.2.2", "react": "^16.13.1", diff --git a/webapp/packages/object-viewer-plugin/package.json b/webapp/packages/object-viewer-plugin/package.json index ce49e3801d..3cd05bdcfb 100644 --- a/webapp/packages/object-viewer-plugin/package.json +++ b/webapp/packages/object-viewer-plugin/package.json @@ -16,14 +16,6 @@ }, "peerDependencies": { "@dbeaver/core": "^0.1.0", - "@material/button": "^4.0.0", - "@material/elevation": "^4.0.0", - "@material/ripple": "^4.0.0", - "@material/theme": "^4.0.0", - "@material/typography": "^4.0.0", - "graphql": "^14.5.8", - "graphql-request": "^1.8.2", - "graphql-tag": "^2.10.1", "mobx": "^5.15.4", "mobx-react": "^6.2.2", "react": "^16.13.1", diff --git a/webapp/packages/sql-editor/package.json b/webapp/packages/sql-editor/package.json index 389744c588..7d6bdd69ef 100644 --- a/webapp/packages/sql-editor/package.json +++ b/webapp/packages/sql-editor/package.json @@ -23,9 +23,6 @@ "peerDependencies": { "@dbeaver/core": "^0.1.0", "@dbeaver/data-viewer-plugin": "^0.1.0", - "graphql": "^14.5.8", - "graphql-request": "^1.8.2", - "graphql-tag": "^2.10.1", "mobx": "^5.15.4", "mobx-react": "^6.2.2", "react": "^16.13.1", diff --git a/webapp/products/default/plugins-list.js b/webapp/products/default/plugins-list.js index bd0cc9065d..19d2e27b30 100644 --- a/webapp/products/default/plugins-list.js +++ b/webapp/products/default/plugins-list.js @@ -4,6 +4,7 @@ module.exports = [ "@dbeaver/administration", + "@dbeaver/authentication", "@dbeaver/ag-grid-plugin", "@dbeaver/basic-connection-plugin", "@dbeaver/custom-connection-plugin", diff --git a/webapp/tsconfig.json b/webapp/tsconfig.json index a9de1f9a6b..1882fdb842 100644 --- a/webapp/tsconfig.json +++ b/webapp/tsconfig.json @@ -19,7 +19,8 @@ "@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"] + "@dbeaver/core/product": ["packages/core/src/product"], + "@dbeaver/core/placeholder": ["packages/core/src/placeholder"] } } }