mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-16 14:50:15 +08:00
Compare commits
11
Commits
release_26_1_0
...
23.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e82eed3e0 | ||
|
|
87f9bd5e49 | ||
|
|
b2fa46097e | ||
|
|
6e736bae41 | ||
|
|
aa288c536b | ||
|
|
a2ebe8bd82 | ||
|
|
f1a75898af | ||
|
|
d3d8983134 | ||
|
|
da3f703639 | ||
|
|
2ad858ea80 | ||
|
|
81c1b67ab7 |
+18
-1
@@ -324,6 +324,9 @@ public class LocalResourceController implements RMController {
|
||||
@Override
|
||||
public RMProject getProject(@NotNull String projectId, boolean readResources, boolean readProperties) throws DBException {
|
||||
RMProject project = makeProjectFromId(projectId, true);
|
||||
if (project == null) {
|
||||
return null;
|
||||
}
|
||||
if (readResources) {
|
||||
doProjectOperation(projectId, () -> {
|
||||
project.setChildren(
|
||||
@@ -870,7 +873,15 @@ public class LocalResourceController implements RMController {
|
||||
var projectName = parseProjectName(projectId);
|
||||
var projectPath = getProjectPath(projectId);
|
||||
if (!Files.exists(projectPath)) {
|
||||
return null;
|
||||
if (isPrivateProject(projectId) && isProjectOwner(projectId)) {
|
||||
try {
|
||||
Files.createDirectories(projectPath);
|
||||
} catch (Exception e) {
|
||||
throw new DBException("Failed to create project " + projectId + ": " + e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Set<RMProjectPermission> permissions = Set.of();
|
||||
if (loadPermissions && credentialsProvider.getActiveUserCredentials() != null) {
|
||||
@@ -1210,6 +1221,12 @@ public class LocalResourceController implements RMController {
|
||||
return RMProjectType.USER.getPrefix().equals(rmProjectName.getPrefix());
|
||||
}
|
||||
|
||||
private boolean isProjectOwner(String projectId) {
|
||||
var activeUserCredentials = credentialsProvider.getActiveUserCredentials();
|
||||
var userId = activeUserCredentials == null ? null : activeUserCredentials.getUserId();
|
||||
return isProjectOwner(projectId, userId);
|
||||
}
|
||||
|
||||
public static boolean isProjectOwner(String projectId, String userId) {
|
||||
RMProjectName rmProjectName = parseProjectNameUnsafe(projectId);
|
||||
return RMProjectType.USER.getPrefix().equals(rmProjectName.getPrefix()) &&
|
||||
|
||||
-11
@@ -17,7 +17,6 @@
|
||||
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.DBWUserIdentity;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.auth.SMAuthProviderExternal;
|
||||
@@ -38,7 +37,6 @@ import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -114,15 +112,6 @@ public class WebSessionAuthProcessor {
|
||||
SMAuthProviderExternal<?> authProviderExternal = authProviderInstance instanceof SMAuthProviderExternal<?> ?
|
||||
(SMAuthProviderExternal<?>) authProviderInstance : null;
|
||||
|
||||
boolean providerDisabled = !isProviderEnabled(providerId);
|
||||
if (configMode || webSession.hasPermission(DBWConstants.PERMISSION_ADMIN)) {
|
||||
// 1. Admin can authorize in any providers
|
||||
// 2. When it authorizes in non-local provider for the first time we force linkUser flag
|
||||
if (providerDisabled && webSession.getUser() != null) {
|
||||
linkWithActiveUser = true;
|
||||
}
|
||||
}
|
||||
|
||||
SMSession authSession;
|
||||
|
||||
if (authProviderExternal != null && !configMode && !alreadyLoggedIn) {
|
||||
|
||||
@@ -222,6 +222,17 @@ public class WebServiceUtils extends WebCommonUtils {
|
||||
@Nullable Map<String, Object> authProperties,
|
||||
boolean saveCredentials,
|
||||
boolean sharedCredentials
|
||||
) {
|
||||
saveAuthProperties(dataSourceContainer, configuration, authProperties, saveCredentials, sharedCredentials, false);
|
||||
}
|
||||
|
||||
public static void saveAuthProperties(
|
||||
@NotNull DBPDataSourceContainer dataSourceContainer,
|
||||
@NotNull DBPConnectionConfiguration configuration,
|
||||
@Nullable Map<String, Object> authProperties,
|
||||
boolean saveCredentials,
|
||||
boolean sharedCredentials,
|
||||
boolean isTest
|
||||
) {
|
||||
dataSourceContainer.setSavePassword(saveCredentials);
|
||||
dataSourceContainer.setSharedCredentials(sharedCredentials);
|
||||
@@ -242,12 +253,14 @@ public class WebServiceUtils extends WebCommonUtils {
|
||||
// Read save credentials
|
||||
DBAAuthCredentials credentials = configuration.getAuthModel().loadCredentials(dataSourceContainer, configuration);
|
||||
|
||||
var currentAuthProps = new HashMap<String, String>();
|
||||
for (Map.Entry<String, Object> stringObjectEntry : authProperties.entrySet()) {
|
||||
var value = stringObjectEntry.getValue() == null ? null : stringObjectEntry.getValue().toString();
|
||||
currentAuthProps.put(stringObjectEntry.getKey(), value);
|
||||
if (isTest) {
|
||||
var currentAuthProps = new HashMap<String, String>();
|
||||
for (Map.Entry<String, Object> stringObjectEntry : authProperties.entrySet()) {
|
||||
var value = stringObjectEntry.getValue() == null ? null : stringObjectEntry.getValue().toString();
|
||||
currentAuthProps.put(stringObjectEntry.getKey(), value);
|
||||
}
|
||||
configuration.setAuthProperties(currentAuthProps);
|
||||
}
|
||||
configuration.setAuthProperties(currentAuthProps);
|
||||
if (!authProperties.isEmpty()) {
|
||||
|
||||
// Make new Gson parser with type adapters to deserialize into existing credentials
|
||||
|
||||
+2
-1
@@ -660,7 +660,8 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
testDataSource.getConnectionConfiguration(),
|
||||
connectionConfig.getCredentials(),
|
||||
true,
|
||||
false
|
||||
false,
|
||||
true
|
||||
);
|
||||
} else {
|
||||
testDataSource = WebServiceUtils.createConnectionFromConfig(connectionConfig, sessionRegistry);
|
||||
|
||||
+5
@@ -68,6 +68,11 @@ public class WebServiceAuthImpl implements DBWServiceAuth {
|
||||
if (CommonUtils.isEmpty(providerId)) {
|
||||
throw new DBWebException("Missing auth provider parameter");
|
||||
}
|
||||
WebAuthProviderDescriptor authProviderDescriptor = WebAuthProviderRegistry.getInstance()
|
||||
.getAuthProvider(providerId);
|
||||
if (authProviderDescriptor.isTrusted()) {
|
||||
throw new DBWebException(authProviderDescriptor.getLabel() + " not allowed for authorization via GQL API");
|
||||
}
|
||||
if (authParameters == null) {
|
||||
authParameters = Map.of();
|
||||
}
|
||||
|
||||
+48
-33
@@ -1217,6 +1217,9 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
|
||||
@Override
|
||||
public SMAuthInfo authenticateAnonymousUser(@NotNull String appSessionId, @NotNull Map<String, Object> sessionParameters, @NotNull SMSessionType sessionType) throws DBException {
|
||||
if (!application.getAppConfiguration().isAnonymousAccessEnabled()) {
|
||||
throw new SMException("Anonymous access restricted");
|
||||
}
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
|
||||
var smSessionId = createSmSession(appSessionId, null, sessionParameters, sessionType, dbCon);
|
||||
@@ -1252,6 +1255,9 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
@Nullable String authProviderConfigurationId,
|
||||
@NotNull Map<String, Object> userCredentials
|
||||
) throws DBException {
|
||||
if (isProviderDisabled(authProviderId, authProviderConfigurationId)) {
|
||||
throw new SMException("Unsupported authentication provider: " + authProviderId);
|
||||
}
|
||||
var authProgressMonitor = new LoggingProgressMonitor(log);
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
|
||||
@@ -1786,40 +1792,41 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
}
|
||||
|
||||
userAuthData.putAll((Map<String, Object>) authInfo.getAuthData().get(authConfiguration));
|
||||
SMAutoAssign autoAssign = isMainAuthSession
|
||||
? getAutoAssignUserData(authProvider, providerConfig, userAuthData, finishAuthMonitor)
|
||||
//do not auto assign user data if it an additional auth
|
||||
: null;
|
||||
if (autoAssign != null) {
|
||||
detectedAuthRole = autoAssign.getAuthRole();
|
||||
}
|
||||
|
||||
var userIdFromCreds = findOrCreateExternalUserByCredentials(
|
||||
authProvider,
|
||||
authAttemptSessionInfo.getSessionParams(),
|
||||
userAuthData,
|
||||
finishAuthMonitor,
|
||||
activeUserId,
|
||||
activeUserId == null,
|
||||
detectedAuthRole,
|
||||
providerConfig
|
||||
);
|
||||
|
||||
if (userIdFromCreds == null) {
|
||||
var error = "Invalid user credentials";
|
||||
updateAuthStatus(authId, SMAuthStatus.ERROR, storedUserData, error);
|
||||
return SMAuthInfo.error(authId, error);
|
||||
}
|
||||
|
||||
if (autoAssign != null && !CommonUtils.isEmpty(autoAssign.getExternalTeamIds())) {
|
||||
if (allTeams == null) {
|
||||
allTeams = readAllTeams();
|
||||
if (isMainAuthSession) {
|
||||
SMAutoAssign autoAssign =
|
||||
getAutoAssignUserData(authProvider, providerConfig, userAuthData, finishAuthMonitor);
|
||||
if (autoAssign != null) {
|
||||
detectedAuthRole = autoAssign.getAuthRole();
|
||||
}
|
||||
autoUpdateUserTeams(authProvider, autoAssign, userIdFromCreds, allTeams);
|
||||
}
|
||||
|
||||
if (activeUserId == null) {
|
||||
activeUserId = userIdFromCreds;
|
||||
var userIdFromCreds = findOrCreateExternalUserByCredentials(
|
||||
authProvider,
|
||||
authAttemptSessionInfo.getSessionParams(),
|
||||
userAuthData,
|
||||
finishAuthMonitor,
|
||||
activeUserId,
|
||||
activeUserId == null,
|
||||
detectedAuthRole,
|
||||
providerConfig
|
||||
);
|
||||
|
||||
if (userIdFromCreds == null) {
|
||||
var error = "Invalid user credentials";
|
||||
updateAuthStatus(authId, SMAuthStatus.ERROR, storedUserData, error);
|
||||
return SMAuthInfo.error(authId, error);
|
||||
}
|
||||
|
||||
if (autoAssign != null && !CommonUtils.isEmpty(autoAssign.getExternalTeamIds())) {
|
||||
if (allTeams == null) {
|
||||
allTeams = readAllTeams();
|
||||
}
|
||||
autoUpdateUserTeams(authProvider, autoAssign, userIdFromCreds, allTeams);
|
||||
}
|
||||
|
||||
if (activeUserId == null) {
|
||||
activeUserId = userIdFromCreds;
|
||||
}
|
||||
}
|
||||
storedUserData.put(authConfiguration,
|
||||
saveSecuredCreds ? userAuthData : filterSecuredUserData(userAuthData, getAuthProvider(authProviderId)));
|
||||
@@ -2585,9 +2592,17 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
return activeUserCredentials.getUserId();
|
||||
}
|
||||
|
||||
private boolean isProviderEnabled(@NotNull String providerId) {
|
||||
private boolean isProviderDisabled(@NotNull String providerId, @Nullable String authConfigurationId) {
|
||||
WebAuthConfiguration appConfiguration = application.getAuthConfiguration();
|
||||
return appConfiguration.isAuthProviderEnabled(providerId);
|
||||
if (!appConfiguration.isAuthProviderEnabled(providerId)) {
|
||||
return true;
|
||||
}
|
||||
if (authConfigurationId != null) {
|
||||
SMAuthProviderCustomConfiguration configuration =
|
||||
appConfiguration.getAuthProviderConfiguration(authConfigurationId);
|
||||
return configuration == null || configuration.isDisabled();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void clearOldAuthAttemptInfo() throws DBException {
|
||||
|
||||
+9
-5
@@ -33,7 +33,7 @@ import java.nio.file.Path;
|
||||
@RunWith(Suite.class)
|
||||
@Suite.SuiteClasses(
|
||||
{
|
||||
PlatformTest.class,
|
||||
ConnectionsTest.class,
|
||||
AuthenticationTest.class,
|
||||
ResourceManagerTest.class,
|
||||
RMLockTest.class
|
||||
@@ -63,10 +63,7 @@ public class CEServerTestSuite {
|
||||
testApp.start(null);
|
||||
});
|
||||
thread.start();
|
||||
client = HttpClient.newBuilder()
|
||||
.cookieHandler(new CookieManager())
|
||||
.version(HttpClient.Version.HTTP_2)
|
||||
.build();
|
||||
client = createClient();
|
||||
long startTime = System.currentTimeMillis();
|
||||
long endTime = 0;
|
||||
while (true) {
|
||||
@@ -97,6 +94,13 @@ public class CEServerTestSuite {
|
||||
return client;
|
||||
}
|
||||
|
||||
public static HttpClient createClient() {
|
||||
return HttpClient.newBuilder()
|
||||
.cookieHandler(new CookieManager())
|
||||
.version(HttpClient.Version.HTTP_2)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static Path getScriptsPath() {
|
||||
return scriptsPath;
|
||||
}
|
||||
|
||||
+6
-2
@@ -30,7 +30,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class PlatformTest {
|
||||
public class ConnectionsTest {
|
||||
|
||||
public static final String GQL_TEMPLATE_CREATE_CONNECTION = "createConnection.json";
|
||||
public static final String GQL_TEMPLATE_DELETE_CONNECTION = "deleteConnection.json";
|
||||
@@ -52,12 +52,16 @@ public class PlatformTest {
|
||||
|
||||
@Test
|
||||
public void testBCreateConnection() throws Exception {
|
||||
HttpClient client = CEServerTestSuite.getClient();
|
||||
HttpClient client = CEServerTestSuite.createClient();
|
||||
WebTestUtils.authenticateUser(
|
||||
client, CEServerTestSuite.getScriptsPath(), CEServerTestSuite.GQL_API_URL);
|
||||
|
||||
List<Map<String, Object>> connections = getUserConnections(client);
|
||||
Map<String, Object> addedConnection = createConnection(client);
|
||||
if (!getUserConnections(client).contains(addedConnection)) {
|
||||
throw new Exception("The new connection was not added");
|
||||
}
|
||||
|
||||
boolean deleteConnection = deleteConnection(client, CommonUtils.toString(addedConnection.get("id")));
|
||||
if (!deleteConnection) {
|
||||
throw new Exception("The new connection was not deleted");
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"query": "\n mutation createConnection($config: ConnectionConfig!) {\n createConnection(config: $config) {\n id\n }\n} ",
|
||||
"query": "\n mutation createConnection($projectId:ID!, $config: ConnectionConfig!) {\n createConnection(projectId:$projectId, config: $config) {\n id\n }\n} ",
|
||||
"variables": {
|
||||
"projectId": "u_test",
|
||||
"config": {
|
||||
"name": "PostgreSQL@localhost (1)",
|
||||
"driverId": "postgresql:postgres-jdbc",
|
||||
|
||||
+8
-5
@@ -1,5 +1,8 @@
|
||||
{"query": "mutation deleteConnection($id: ID!) {deleteConnection(id: $id)}",
|
||||
"variables": {
|
||||
"id" : "${connectionId}"
|
||||
},
|
||||
"operationName":"deleteConnection"}
|
||||
{
|
||||
"query": "mutation deleteConnection($projectId:ID!, $id: ID!) {deleteConnection(projectId:$projectId, id: $id)}",
|
||||
"variables": {
|
||||
"projectId": "u_test",
|
||||
"id": "${connectionId}"
|
||||
},
|
||||
"operationName": "deleteConnection"
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
CachedMapAllKey,
|
||||
CachedMapResource,
|
||||
GraphQLService,
|
||||
ResourceKey,
|
||||
resourceKeyList,
|
||||
} from '@cloudbeaver/core-sdk';
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ export class UserInfoResource extends CachedDataResource<UserInfo | null, void,
|
||||
constructor(
|
||||
private readonly graphQLService: GraphQLService,
|
||||
private readonly authProviderService: AuthProviderService,
|
||||
private readonly sessionResource: SessionResource,
|
||||
sessionResource: SessionResource,
|
||||
private readonly sessionDataResource: SessionDataResource,
|
||||
) {
|
||||
super(() => null, undefined, ['customIncludeOriginDetails', 'includeConfigurationParameters']);
|
||||
|
||||
@@ -4,4 +4,5 @@
|
||||
|
||||
display: block;
|
||||
text-transform: none;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ export interface CheckboxBaseProps {
|
||||
mod?: CheckboxMod[];
|
||||
ripple?: boolean;
|
||||
indeterminate?: boolean;
|
||||
inverse?: boolean;
|
||||
style?: ComponentStyle;
|
||||
}
|
||||
|
||||
@@ -62,6 +63,7 @@ export const Checkbox: CheckboxType = observer(function Checkbox({
|
||||
checked,
|
||||
defaultChecked,
|
||||
children,
|
||||
inverse,
|
||||
mod,
|
||||
ripple,
|
||||
className,
|
||||
@@ -77,6 +79,7 @@ export const Checkbox: CheckboxType = observer(function Checkbox({
|
||||
defaultChecked,
|
||||
state,
|
||||
name,
|
||||
inverse,
|
||||
onChange,
|
||||
});
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ const switchMod = {
|
||||
interface IBaseProps {
|
||||
mod?: Array<keyof typeof switchMod>;
|
||||
description?: React.ReactNode;
|
||||
inverse?: boolean;
|
||||
}
|
||||
|
||||
interface SwitchType {
|
||||
@@ -45,10 +46,11 @@ export const Switch: SwitchType = observer(function Switch({
|
||||
defaultChecked,
|
||||
className,
|
||||
children,
|
||||
onChange,
|
||||
inverse,
|
||||
mod = [],
|
||||
autoHide,
|
||||
disabled,
|
||||
onChange,
|
||||
...rest
|
||||
}: IBaseProps & (ICheckboxControlledProps | ICheckboxObjectProps<any>)) {
|
||||
const checkboxState = useCheckboxState({
|
||||
@@ -58,6 +60,7 @@ export const Switch: SwitchType = observer(function Switch({
|
||||
defaultChecked,
|
||||
state,
|
||||
name,
|
||||
inverse,
|
||||
onChange,
|
||||
});
|
||||
rest = filterLayoutFakeProps(rest);
|
||||
|
||||
@@ -17,6 +17,7 @@ export type CheckboxStateOptions<TKey extends string> = {
|
||||
defaultValue: string | undefined;
|
||||
checked: boolean | undefined;
|
||||
defaultChecked: boolean | undefined;
|
||||
inverse: boolean | undefined;
|
||||
} & (
|
||||
| {
|
||||
state: undefined;
|
||||
@@ -56,12 +57,20 @@ export function useCheckboxState<TKey extends string>(options: CheckboxStateOpti
|
||||
}
|
||||
}
|
||||
|
||||
if (options.inverse) {
|
||||
checked = !checked;
|
||||
}
|
||||
|
||||
return useObjectRef<ICheckboxState>(
|
||||
() => ({
|
||||
checked,
|
||||
change(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
const { state, name, value, onChange, count, context } = optionsRef;
|
||||
const checked = event.target.checked;
|
||||
const { inverse, state, name, value, onChange, count, context } = optionsRef;
|
||||
let checked = event.target.checked;
|
||||
|
||||
if (inverse) {
|
||||
checked = !checked;
|
||||
}
|
||||
|
||||
if (state !== undefined && name !== undefined) {
|
||||
const currentState = state[name as TKey];
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { PermissionsService } from '@cloudbeaver/core-root';
|
||||
import { SessionPermissionsResource } from '@cloudbeaver/core-root';
|
||||
|
||||
import { useResource } from './ResourcesHooks/useResource';
|
||||
|
||||
export function usePermission(key: string): boolean {
|
||||
const permissionsService = useService(PermissionsService);
|
||||
const sessionPermissionsResource = useResource(usePermission, SessionPermissionsResource, undefined);
|
||||
|
||||
return permissionsService.has(key);
|
||||
return sessionPermissionsResource.data.has(key);
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ export class ConnectionNavNodeService extends Dependency {
|
||||
connection = await this.connectionsManagerService.requireConnection(createConnectionParam(connection));
|
||||
|
||||
if (!connection?.connected) {
|
||||
throw new Error('Connection not established');
|
||||
ExecutorInterrupter.interrupt(contexts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,6 +198,15 @@ export class NotificationService {
|
||||
console.error(exception);
|
||||
}
|
||||
|
||||
throwSilently(exception: Error | GQLError | undefined | null): void {
|
||||
this.logError({
|
||||
title: '',
|
||||
details: exception,
|
||||
isSilent: true,
|
||||
});
|
||||
throw exception;
|
||||
}
|
||||
|
||||
close(id: number, delayDeleting = true): void {
|
||||
// TODO: emit event or something
|
||||
|
||||
|
||||
@@ -91,6 +91,11 @@ export class DatabaseDataActions<TOptions, TResult extends IDatabaseDataResult>
|
||||
const result = results.find(result => result.uniqueResultId === key);
|
||||
|
||||
for (const action of actions) {
|
||||
if (!(action.constructor as any).dataFormat.includes(result?.dataFormat)) {
|
||||
this.actions.delete(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
action.updateResults(results);
|
||||
|
||||
if (!result) {
|
||||
|
||||
+3
-1
@@ -236,7 +236,9 @@ export class ConnectionSchemaManagerBootstrap extends Bootstrap {
|
||||
|
||||
this.menuService.addCreator({
|
||||
menus: [MENU_CONNECTION_DATA_CONTAINER_SELECTOR],
|
||||
isApplicable: () => this.connectionSchemaManagerService.isObjectCatalogChangeable && !!this.connectionSchemaManagerService.objectContainerList,
|
||||
isApplicable: () =>
|
||||
(this.connectionSchemaManagerService.isObjectCatalogChangeable || this.connectionSchemaManagerService.isObjectSchemaChangeable) &&
|
||||
!!this.connectionSchemaManagerService.objectContainerList,
|
||||
getItems: (context, items) => {
|
||||
items = [...items];
|
||||
|
||||
|
||||
+13
-4
@@ -153,6 +153,15 @@ export function useElementsTree(options: IOptions): IElementsTree {
|
||||
options.expandStateGetters = useMemo(() => options.expandStateGetters || [], [...(options.expandStateGetters || [])]);
|
||||
const state = options.localState || localTreeNodesState;
|
||||
|
||||
async function handleLoadChildren(id: string, manual: boolean): Promise<boolean> {
|
||||
try {
|
||||
return await options.loadChildren(id, manual);
|
||||
} catch (exception: any) {
|
||||
notificationService.logException(exception);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const functionsRef = useObjectRef({
|
||||
async loadTree(nodeId: string) {
|
||||
elementsTree.loading = true;
|
||||
@@ -194,7 +203,7 @@ export function useElementsTree(options: IOptions): IElementsTree {
|
||||
return;
|
||||
}
|
||||
|
||||
const loaded = await options.loadChildren(child, false);
|
||||
const loaded = await handleLoadChildren(child, false);
|
||||
|
||||
if (!loaded) {
|
||||
const node = navNodeInfoResource.get(child);
|
||||
@@ -530,7 +539,7 @@ export function useElementsTree(options: IOptions): IElementsTree {
|
||||
if (!leaf && this.settings?.foldersTree && expandableOrExpanded) {
|
||||
const nodeId = node.id;
|
||||
|
||||
const loaded = await options.loadChildren(node.id, true);
|
||||
const loaded = await handleLoadChildren(node.id, false);
|
||||
if (loaded) {
|
||||
this.setFilter('');
|
||||
options.folderExplorer.open(path, nodeId);
|
||||
@@ -549,7 +558,7 @@ export function useElementsTree(options: IOptions): IElementsTree {
|
||||
|
||||
try {
|
||||
if (state || (this.filtering && !treeNodeState.showInFilter)) {
|
||||
state = await options.loadChildren(node.id, true);
|
||||
state = await handleLoadChildren(node.id, true);
|
||||
}
|
||||
|
||||
if (this.filtering) {
|
||||
@@ -611,7 +620,7 @@ export function useElementsTree(options: IOptions): IElementsTree {
|
||||
async loadPath(path: string[], lastNode?: string): Promise<string | undefined> {
|
||||
let lastLoadedNode: string | undefined;
|
||||
for (const nodeId of path) {
|
||||
const loaded = await options.loadChildren(nodeId, false);
|
||||
const loaded = await handleLoadChildren(nodeId, false);
|
||||
|
||||
if (!loaded) {
|
||||
return lastLoadedNode;
|
||||
|
||||
+31
-41
@@ -72,53 +72,43 @@ export class NavigationTreeService extends View<string> {
|
||||
await this.navNodeManagerService.navToNode(id, parentId);
|
||||
}
|
||||
|
||||
async loadNestedNodes(id = ROOT_NODE_PATH, tryConnect?: boolean, notify = true): Promise<boolean> {
|
||||
try {
|
||||
if (this.isConnectionNode(id)) {
|
||||
let connection = this.connectionInfoResource.getConnectionForNode(id);
|
||||
async loadNestedNodes(id = ROOT_NODE_PATH, tryConnect?: boolean): Promise<boolean> {
|
||||
if (this.isConnectionNode(id)) {
|
||||
let connection = this.connectionInfoResource.getConnectionForNode(id);
|
||||
|
||||
if (connection) {
|
||||
connection = await this.connectionInfoResource.load(createConnectionParam(connection));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!connection.connected) {
|
||||
if (!tryConnect) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const connected = await this.tryInitConnection(createConnectionParam(connection));
|
||||
if (!connected) {
|
||||
return false;
|
||||
}
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await this.navTreeResource.waitLoad();
|
||||
|
||||
if (tryConnect && this.navTreeResource.getException(id)) {
|
||||
this.navTreeResource.markOutdated(id);
|
||||
}
|
||||
|
||||
const parents = this.navNodeInfoResource.getParents(id);
|
||||
|
||||
if (parents.length > 0 && !this.navNodeInfoResource.has(id)) {
|
||||
if (connection) {
|
||||
connection = await this.connectionInfoResource.load(createConnectionParam(connection));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.navTreeResource.load(CachedResourcePageKey(CACHED_RESOURCE_DEFAULT_PAGE_OFFSET, this.navTreeResource.childrenLimit).setTarget(id));
|
||||
return true;
|
||||
} catch (exception: any) {
|
||||
if (notify) {
|
||||
this.notificationService.logException(exception);
|
||||
if (!connection.connected) {
|
||||
if (!tryConnect) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const connected = await this.tryInitConnection(createConnectionParam(connection));
|
||||
if (!connected) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
await this.navTreeResource.waitLoad();
|
||||
|
||||
if (tryConnect && this.navTreeResource.getException(id)) {
|
||||
this.navTreeResource.markOutdated(id);
|
||||
}
|
||||
|
||||
const parents = this.navNodeInfoResource.getParents(id);
|
||||
|
||||
if (parents.length > 0 && !this.navNodeInfoResource.has(id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.navTreeResource.load(CachedResourcePageKey(CACHED_RESOURCE_DEFAULT_PAGE_OFFSET, this.navTreeResource.childrenLimit).setTarget(id));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
selectNode(id: string, multiple?: boolean): void {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
import { useObjectRef } from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { NotificationService } from '@cloudbeaver/core-events';
|
||||
import type { NavNode } from '@cloudbeaver/core-navigation-tree';
|
||||
|
||||
import { NavigationTreeService } from './NavigationTreeService';
|
||||
@@ -22,13 +23,19 @@ const bindActions: Array<keyof INavigationTree> = ['handleOpen', 'handleSelect',
|
||||
|
||||
export function useNavigationTree(): INavigationTree {
|
||||
const navigationTreeService = useService(NavigationTreeService);
|
||||
const notificationService = useService(NotificationService);
|
||||
|
||||
return useObjectRef<INavigationTree>(
|
||||
() => ({
|
||||
navigationTreeService,
|
||||
async handleOpen(node: NavNode, folder: boolean) {
|
||||
if (!folder) {
|
||||
await this.navigationTreeService.navToNode(node.id, node.parentId);
|
||||
try {
|
||||
await this.navigationTreeService.navToNode(node.id, node.parentId);
|
||||
} catch (exception: any) {
|
||||
notificationService.logException(exception);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
},
|
||||
handleSelect(node: NavNode, state: boolean) {
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
getRealExecutionContextId,
|
||||
} from '@cloudbeaver/core-connections';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { NotificationService } from '@cloudbeaver/core-events';
|
||||
import { NodeManagerUtils } from '@cloudbeaver/core-navigation-tree';
|
||||
|
||||
import type { ISqlEditorTabState } from './ISqlEditorTabState';
|
||||
@@ -50,6 +51,7 @@ export const SqlEditorOverlay = observer<Props>(function SqlEditorOverlay({ stat
|
||||
const translate = useTranslate();
|
||||
const sqlEditorService = useService(SqlEditorService);
|
||||
const sqlDataSourceService = useService(SqlDataSourceService);
|
||||
const notificationService = useService(NotificationService);
|
||||
const dataSource = sqlDataSourceService.get(state.editorId);
|
||||
const executionContextId = dataSource?.executionContext?.id;
|
||||
const executionContext = dataSource?.executionContext;
|
||||
@@ -77,7 +79,11 @@ export const SqlEditorOverlay = observer<Props>(function SqlEditorOverlay({ stat
|
||||
}
|
||||
|
||||
async function init() {
|
||||
await sqlEditorService.initEditorConnection(state);
|
||||
try {
|
||||
await sqlEditorService.initEditorConnection(state);
|
||||
} catch (exception: any) {
|
||||
notificationService.logException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
const dataContainer = getComputed(() =>
|
||||
|
||||
Reference in New Issue
Block a user