Compare commits

...
33 Commits
Author SHA1 Message Date
alexandDaria Marutkina 2f120d6a4f Cb 5819 administration tables redesign (#3033)
* CB-5819 add user panel

* CB-5819 redesign teams

* CB-5819 remove extra styles

* CB-5819 review fixes

* CB-5819 revert group title

* CB-5819 remove code duplication

* CB-5819 fix import

* CB-5819 move user table loader

* CB-5819 keep loader mounted

* CB-5819 truncate titles

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2024-10-29 20:43:36 +01:00
Ainur d3be30f55b CB-5623 fix npe (#3030) 2024-10-29 16:12:10 +00:00
alexandDaria Marutkina 2c7df05c45 CB-3446 add d3 imports (#3025)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2024-10-29 10:05:46 +03:00
Ainur 11167d5802 CB-5683 change password for disabled user fix (#3032) 2024-10-28 15:24:38 +00:00
sergeyteleshevandDaria Marutkina 4cfb27b33b CB-5645 [EE\AWS] Change the instruction in the Version update tab (#3017)
* CB-5645 adds core-links and GithubLinks

* CB-5645 adds BaseUpdate instruction to EE, TE, AWS

* CB-5645 pr fixes

* CB-5645 REVERT IT (need for tests)

* Revert "CB-5645 REVERT IT (need for tests)"

This reverts commit 6c53b907c7.

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2024-10-25 13:17:30 +03:00
sergeyteleshevandEvgenia 0126b204c0 CB-5566 fixes checkboxes for tables (#3026)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2024-10-24 18:34:41 +03:00
alexandDaria Marutkina 6dbcfe408e CB-5745 explicitly set word break on field description (#3027)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2024-10-24 14:31:02 +02:00
Serge Rider de2d9f029f CB-5732 Navigator filters fix for optional nodes (#3024) 2024-10-24 12:33:34 +02:00
Elizabeth 2f895f6dab dbeaver/pro#3419 Don't load additional info on typing with auto activation of completion (#3022) 2024-10-24 12:10:08 +02:00
sergeyteleshevandDaria Marutkina c7af629d8a CB-5712 adds loaders for administration menu items (#3011)
* CB-5712 adds loaders for administration menu items

* CB-5712 pr fixes

* CB-5712 pr fix for preloading serverConfigResource in constructor

* CB-5712 removes preload from AuthProviderResource

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2024-10-24 11:04:23 +03:00
sergeyteleshevandmr-anton-t 1782ce45a7 CB-5793 removes extra space for table cells (#3013)
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2024-10-24 11:04:00 +03:00
51e0218682 CB-5575 pass column key along with row (#3004)
* CB-5575 pass column key along with row

* CB-5575 change arg

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2024-10-24 11:03:30 +03:00
Ainur e08353f13f CB-5792 add header for static content (#3018) 2024-10-23 15:46:26 +00:00
AinurandDenisSinelnikov 383952c60e CB-5838 check all row identifiers fix (#3014)
Co-authored-by: DenisSinelnikov <142215442+DenisSinelnikov@users.noreply.github.com>
2024-10-23 15:45:13 +00:00
alexandDaria Marutkina 5282175b7c CB-5701 allow to choose provider by keyboard (#3008)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2024-10-22 13:21:49 +03:00
alexandDaria Marutkina b4db34ea48 CB-5706 do not increase height (#3002)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2024-10-21 13:25:10 +03:00
Evgenia 9672e5b3b4 Merge pull request #3015 from dbeaver/EvgeniaBzzz-patch-1
Update README.md - CB RN 24.2.3
2024-10-21 12:27:20 +03:00
Evgenia 48a7a4f806 Update README.md - RN 24.3.2 2024-10-21 12:16:04 +03:00
Anastasiya cbceee6ec3 dbeaver/tech-docs#654 update gql comments, remove outdated info (#3012) 2024-10-18 18:38:56 +02:00
sergeyteleshev ffe0a18828 Cb 5757 unit tests for core utils and core blocks (#2961)
* CB-5757 adds unit tests for getDomainFromUrl helper

* CB-5757 adds unit tests for getTextFileReadingProcess helper

* CB-5757 adds unit tests for TextTools helper

* CB-5757 adds unit tests for downloadFromURL helper

* CB-5757 adds unit tests for Deferred and PromiseExecutor

* CB-5757 adds unit tests for core-blocks hooks

* CB-5757 fixes the tests
2024-10-18 08:34:19 +03:00
alex 115cfb3bde Cb 5753 u tests (#2964)
* CB-5753 add localization service test, fix grammar

* CB-5753 add test for the getDomainFromUrl

* CB-5753 add test for the client activity service

* CB-5753 add NodeManagerUtils tests

* CB-5753 add more cases

* CB-5753 pass args correctly
2024-10-18 08:33:39 +03:00
Serge Rider e62538b583 Merge remote-tracking branch 'origin/devel' into devel 2024-10-18 04:49:04 +02:00
sergeyteleshev 5255e9949d CB-5820 fixes frontend build script (#3006)
* CB-5820 fixes frontend build script

* CB-5820 fixes test for windows

* CB-5820 cleanup

* CB-5820 reverts back tests for package
2024-10-17 18:56:24 +03:00
Alexey 8e4faa5935 dbeaver/dbeaver-vscode#42 disable artifacts (#3007) 2024-10-17 14:52:18 +02:00
serge-rider 39ad2994b2 Manifests fix 2024-10-17 10:17:55 +02:00
Serge Rider 00788615e1 Merge remote-tracking branch 'origin/devel' into devel 2024-10-17 07:07:34 +02:00
ac937698bd dbeaver/pro#3393 Add "UDBT" feature (#3000)
Co-authored-by: Serge Rider <serge@jkiss.org>
Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2024-10-16 10:33:25 +02:00
Alexey be88091950 dbeaver/dbeaver-devops#1525 update ci workflows (#2998)
* dbeaver/dbeaver-devops#1525 update ci workflows

* dbeaver/dbeaver-devops#1525 fix frontend lint

* dbeaver/dbeaver-devops#1525 move checkstyle config from dbeaver repo

* dbeaver/dbeaver-devops#1525 use own maven setup action

* dbeaver/dbeaver-devops#1525 reuse workflows

* dbeaver/dbeaver-devops#1525 fix concurrency

* dbeaver/dbeaver-devops#1525 fix concurrency
2024-10-16 10:28:44 +02:00
Serge Rider 9dcaec0c09 Merge branch 'devel' of https://github.com/dbeaver/cloudbeaver into devel 2024-10-16 07:01:46 +02:00
f677f48890 CB-5710 return anonymous user info (#2972)
* CB-5710 return anonymous user info

* CB-5710 support anonymous user

* CB-5710 anonymous access disabled active user fix

* CB-5710 simplify logic

* CB-5710 fix error messages

* CB-5710 review fixes

* CB-5710 add computed

* CB-5710 change getters to methods

* CB-5710 fixes after review

---------

Co-authored-by: naumov <iamemptyhuh@gmail.com>
Co-authored-by: alex <48489896+devnaumov@users.noreply.github.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: Serge Rider <serge@jkiss.org>
2024-10-16 07:01:37 +02:00
Serge Rider a44cadbb8a Merge branch 'devel' of https://github.com/dbeaver/cloudbeaver into devel 2024-10-16 06:46:59 +02:00
5c992dc8ee CB-5811 applies new design styles for selects, textareas with disabled or readonly prop (#2999)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2024-10-16 06:46:55 +02:00
Serge Rider 05f85db7e6 dbeaver/pro#3468 24.2.4 version bump 2024-10-15 20:37:32 +02:00
190 changed files with 1863 additions and 559 deletions
+18 -32
View File
@@ -1,20 +1,24 @@
name: Backend Build
on:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Allows you to reuse workflows by referencing their YAML files
workflow_call:
jobs:
build-backend:
name: Backend
name: Build
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
path: cloudbeaver
- name: Clone Deps Repositories
uses: dbeaver/github-actions/clone-repositories@devel
with:
project_deps_path: "./cloudbeaver/project.deps"
- name: Set up JDK 17
uses: actions/setup-java@v4
@@ -23,34 +27,16 @@ jobs:
java-version: "17"
cache: maven
- uses: stCarolas/setup-maven@v5
with:
maven-version: 3.9.0
- name: Give permissions
run: |
sudo chmod 777 ../
shell: bash
- name: Determine branches
id: determine-branch
run: |
echo "pr_branch=${{ github.head_ref }}" >> $GITHUB_ENV
echo "base_branch=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV
- name: Clone dbeaver/dbeaver
id: clone-repo
run: |
git clone -b ${{ env.pr_branch }} https://github.com/dbeaver/dbeaver.git ../dbeaver || git clone -b ${{ env.base_branch }} https://github.com/dbeaver/dbeaver.git ../dbeaver
- uses: dbeaver/github-actions/install-maven@devel
- name: Run build script
run: ./build-backend.sh
shell: bash
working-directory: ./deploy
working-directory: ./cloudbeaver/deploy
- name: Archive build artifacts
uses: actions/upload-artifact@v4
with:
name: backend-build-artifacts
path: deploy/cloudbeaver
if-no-files-found: error
# - name: Archive build artifacts
# uses: actions/upload-artifact@v4
# with:
# name: backend-build-artifacts
# path: cloudbeaver/deploy/cloudbeaver
# if-no-files-found: error
-40
View File
@@ -1,40 +0,0 @@
name: Backend Lint
on:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Allows you to reuse workflows by referencing their YAML files
workflow_call:
inputs:
skip_cache:
required: false
type: string
jobs:
lint:
name: Backend
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Checkout checkstyle config repository
uses: actions/checkout@v4
with:
repository: dbeaver/dbeaver
path: dbeaver-config
- name: Copy checkstyle config
run: cp dbeaver-config/dbeaver-checkstyle-config.xml ./dbeaver-checkstyle-config.xml
- name: Remove checkstyle config directory
run: rm -rf dbeaver-config
- uses: dbelyaev/action-checkstyle@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
reporter: github-pr-review
filter_mode: diff_context
checkstyle_config: ./dbeaver-checkstyle-config.xml
fail_on_error: true
+7 -50
View File
@@ -1,57 +1,14 @@
name: Cleanup checks
name: Cleanup
on:
pull_request:
types: [closed]
push:
branches:
- devel
jobs:
delete-caches:
runs-on: ubuntu-latest
steps:
- name: Cleanup
run: |
gh extension install actions/gh-actions-cache
echo "Fetching list of cache key"
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 )
## Setting this to not fail the workflow while deleting cache keys.
set +e
echo "Deleting caches..."
for cacheKey in $cacheKeysForPR
do
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
done
echo "Done"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge
# delete-docker-image:
# name: Delete Docker Image
# if: github.event.pull_request.merged == true
# runs-on: ubuntu-latest
# steps:
# - name: Check out the repository
# uses: actions/checkout@v4
# - name: Set up Docker Buildx
# uses: docker/setup-buildx-action@v3
# - name: Determine Docker Image Tag
# run: |
# REPO_NAME=$(basename ${{ github.repository }})
# IMAGE_NAME=ghcr.io/${{ github.repository_owner }}/$REPO_NAME
# BRANCH_NAME=${{ github.event.pull_request.head.ref }}
# TAG_NAME=$(echo $BRANCH_NAME | sed 's/[^a-zA-Z0-9._-]/-/g')
# echo "image=$IMAGE_NAME:$TAG_NAME" >> $GITHUB_ENV
# - name: Log in to GitHub Container Registry
# run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
# - name: Delete Docker Image
# run: |
# docker rmi ${{ env.image }}
# echo "Deleted image: ${{ env.image }}"
name: Cleanup
uses: dbeaver/dbeaver-common/.github/workflows/cleanup-caches.yml@devel
secrets: inherit
+16 -31
View File
@@ -1,9 +1,6 @@
name: Frontend Build
on:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Allows you to reuse workflows by referencing their YAML files
workflow_call:
outputs:
@@ -20,8 +17,11 @@ on:
jobs:
frontend-build:
name: Frontend
name: Build
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
outputs:
build-status: ${{ steps.build.outcome }}
@@ -34,6 +34,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- run: corepack enable
- uses: actions/setup-node@v4
with:
node-version: "20"
@@ -58,34 +59,18 @@ jobs:
- name: yarn clean
if: env.skip_cache == 'true'
uses: borales/actions-yarn@v5
with:
dir: webapp
cmd: clean
run: yarn clear
- name: yarn install --frozen-lockfile
uses: borales/actions-yarn@v5
with:
dir: webapp
cmd: install
- run: yarn install --immutable
- name: build
id: build
uses: borales/actions-yarn@v5
with:
dir: webapp/packages/product-default
cmd: bundle
- run: yarn bundle
working-directory: ./webapp/packages/product-default
- name: test
id: test
uses: borales/actions-yarn@v5
with:
dir: webapp
cmd: test
- run: yarn test
- name: Archive build artifacts
uses: actions/upload-artifact@v4
with:
name: frontend-build-artifacts
path: webapp/packages/product-default/lib
if-no-files-found: error
# - name: Archive build artifacts
# uses: actions/upload-artifact@v4
# with:
# name: frontend-build-artifacts
# path: webapp/packages/product-default/lib
# if-no-files-found: error
+18 -18
View File
@@ -1,20 +1,25 @@
name: Frontend Lint
on:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Allows you to reuse workflows by referencing their YAML files
workflow_call:
jobs:
lint:
name: Frontend
name: Lint
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
defaults:
run:
working-directory: ./webapp
steps:
- uses: actions/checkout@v4
- run: corepack enable
- uses: actions/setup-node@v4
with:
node-version: "20"
@@ -29,17 +34,12 @@ jobs:
restore-keys: |
${{ runner.os }}-node_modules-
- name: yarn install --frozen-lockfile
uses: borales/actions-yarn@v5
with:
dir: webapp
cmd: install
- name: Lint
uses: reviewdog/action-eslint@v1
with:
reporter: github-pr-review
filter_mode: file
workdir: webapp
fail_on_error: true
eslint_flags: "--ext .ts,.tsx"
- run: |
yarn install --immutable
git fetch origin "${{ github.base_ref }}" --depth=1
FILES=$(git diff --name-only 'origin/${{ github.base_ref }}' ${{ github.sha }} -- . | sed 's|^webapp/||')
if [ -n "$FILES" ]; then
yarn lint --pass-on-no-patterns --no-error-on-unmatched-pattern $FILES
else
echo "No files to lint"
fi
@@ -1,40 +1,38 @@
name: Check
name: CI
on:
push:
branches:
- devel
pull_request:
branches:
- devel
types:
- opened
- synchronize
- reopened
push:
branches: [devel]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
inputs:
skip_cache:
description: "Skip cache restoration"
required: false
default: "false"
concurrency:
group: ${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'push-pr-devel' }}
cancel-in-progress: true
jobs:
call-backend-build:
name: Build
build-server:
name: Server
uses: ./.github/workflows/backend-build.yml
secrets: inherit
call-frontend-build:
name: Build
build-frontend:
name: Frontend
uses: ./.github/workflows/frontend-build.yml
with:
skip_cache: ${{ github.event.inputs.skip_cache }}
secrets: inherit
call-frontend-lint:
name: Lint
needs: call-frontend-build
lint-server:
name: Server
uses: dbeaver/dbeaver-common/.github/workflows/java-checkstyle.yml@devel
secrets: inherit
lint-frontend:
name: Frontend
uses: ./.github/workflows/frontend-lint.yml
call-backend-lint:
name: Lint
uses: ./.github/workflows/backend-lint.yml
secrets: inherit
# call-frontend-tests:
# name: Frontend Unit Tests
+1 -1
View File
@@ -46,6 +46,6 @@
"*.css": "postcss"
},
"java.checkstyle.configuration": "${workspaceFolder}/../dbeaver/dbeaver-checkstyle-config.xml",
"java.checkstyle.configuration": "${workspaceFolder}/../dbeaver-common/.github/dbeaver-checkstyle-config.xml",
"java.checkstyle.version": "10.12.0"
}
+9
View File
@@ -25,6 +25,15 @@ You can see a live demo of CloudBeaver here: https://demo.cloudbeaver.io
## Changelog
### 24.2.3. 2024-10-21
- Important update:
- Connections Templates feature is declared as obsolete and will be removed in future releases.
- General:
- Data editor enhancements: Rows with focused cells are specially marked to make it easier to locate a position in large tables;
- DB2i driver has been updated to version 20.0.7;
- The URL mode for PostgreSQL now supports connecting to multiple databases;
- The issue with displaying BLOB data types in DuckDB has been resolved.
### 24.2.2. 2024-10-07
- Schemas were added to the SQL autocompletion for PostgreSQL, H2, and SQL Server;
- CloudBeaver can now correctly display negative dates for MySQL database;
+20 -7
View File
@@ -41,22 +41,35 @@ copy ..\config\DefaultConfiguration\GlobalConfiguration\.dbeaver\data-sources.js
move drivers cloudbeaver >NUL
echo Build static content
echo "Build static content"
cd ..\
mkdir .\cloudbeaver\web
cd ..\cloudbeaver\webapp
cd ..\webapp
call yarn
call yarn lerna bootstrap
call yarn lerna run bundle --no-bail --stream --scope=@cloudbeaver/product-default &::-- -- --env source-map
cd .\packages\product-default
call yarn run bundle
if %ERRORLEVEL% neq 0 (
echo 'Application build failed'
exit /b %ERRORLEVEL%
)
cd ..\..\
call yarn test
if %ERRORLEVEL% neq 0 (
echo 'Frontend tests failed'
exit /b %ERRORLEVEL%
)
cd ..\deploy
echo Copy static content
echo "Copy static content"
xcopy /E /Q ..\webapp\packages\product-default\lib cloudbeaver\web >NUL
echo Cloudbeaver is ready. Run run-server.bat in cloudbeaver folder to start the server.
echo "Cloudbeaver is ready. Run run-server.bat in cloudbeaver folder to start the server."
pause
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Model
Bundle-SymbolicName: io.cloudbeaver.model;singleton:=true
Bundle-Version: 1.0.63.qualifier
Bundle-Release-Date: 20241021
Bundle-Version: 1.0.64.qualifier
Bundle-Release-Date: 20241104
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
+1 -1
View File
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.model</artifactId>
<version>1.0.63-SNAPSHOT</version>
<version>1.0.64-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -84,14 +84,18 @@ public abstract class BaseWebAppConfiguration implements WebAppConfiguration {
return resourceManagerEnabled;
}
@Override
public boolean isFeatureEnabled(String id) {
return ArrayUtils.contains(getEnabledFeatures(), id);
}
@Override
public boolean isFeaturesEnabled(String[] features) {
return ArrayUtils.containsAll(getEnabledFeatures(), features);
}
@NotNull
@Override
public String[] getEnabledFeatures() {
if (enabledFeatures == null) {
// No config - enable all features (+backward compatibility)
@@ -44,6 +44,11 @@ public interface WebAppConfiguration {
boolean isFeatureEnabled(String id);
@NotNull
default String[] getEnabledFeatures() {
return new String[0];
}
default boolean isSupportsCustomConnections() {
return true;
}
@@ -48,7 +48,7 @@ public class FSUtils {
public static DBNPathBase getNodeByPath(@NotNull WebSession webSession, @NotNull String nodePath) throws DBException {
DBRProgressMonitor monitor = webSession.getProgressMonitor();
DBNModel navigatorModel = webSession.getNavigatorModel();
DBNModel navigatorModel = webSession.getNavigatorModelOrThrow();
DBNNode node = navigatorModel.getNodeByPath(monitor, nodePath);
if (!(node instanceof DBNPathBase dbnPath)) {
throw new DBWebException("Node '" + nodePath + "' is not found in File Systems");
@@ -399,10 +399,18 @@ public class WebSession extends BaseWebSession
this.locale = locale != null ? locale : Locale.getDefault().getLanguage();
}
@Nullable
public DBNModel getNavigatorModel() {
return navigatorModel;
}
@NotNull
public DBNModel getNavigatorModelOrThrow() throws DBWebException {
if (navigatorModel != null) {
return navigatorModel;
}
throw new DBWebException("Navigator model is not found in session");
}
/**
* Returns and clears progress messages
*/
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Community Product
Bundle-SymbolicName: io.cloudbeaver.product.ce;singleton:=true
Bundle-Version: 24.2.3.qualifier
Bundle-Release-Date: 20241021
Bundle-Version: 24.2.4.qualifier
Bundle-Release-Date: 20241104
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.product.ce</artifactId>
<version>24.2.3-SNAPSHOT</version>
<version>24.2.4-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -2,8 +2,8 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Base JDBC drivers
Bundle-SymbolicName: io.cloudbeaver.resources.drivers.base;singleton:=true
Bundle-Version: 1.0.108.qualifier
Bundle-Release-Date: 20241021
Bundle-Version: 1.0.109.qualifier
Bundle-Release-Date: 20241104
Bundle-Vendor: DBeaver Corp
Bundle-ActivationPolicy: lazy
Automatic-Module-Name: io.cloudbeaver.resources.drivers.base
@@ -9,6 +9,6 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.resources.drivers.base</artifactId>
<version>1.0.108-SNAPSHOT</version>
<version>1.0.109-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Server
Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true
Bundle-Version: 24.2.3.qualifier
Bundle-Release-Date: 20241021
Bundle-Version: 24.2.4.qualifier
Bundle-Release-Date: 20241104
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-ActivationPolicy: lazy
Bundle-Activator: io.cloudbeaver.server.CBPlatformActivator
+1 -1
View File
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.server</artifactId>
<version>24.2.3-SNAPSHOT</version>
<version>24.2.4-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -514,18 +514,21 @@ input ConnectionConfig {
# Host, port, serverName, databaseName are also stored in mainPropertyValues for custom pages
mainPropertyValues: Object @since(version: "24.1.2")
# Connection url jdbc:{driver}://{host}[:{port}]/[{database}]
# Return connection URL jdbc:{driver}://{host}[:{port}]/[{database}]
url: String
# Properties
# Return properties list
properties: Object
# Keep-Alive interval
# Return keep-alive interval
keepAliveInterval: Int
# Return auto-commit connection state
autocommit: Boolean
# Template connection
# Return template connection state
template: Boolean
# Read-onyl connection
# Return read-only connection state
readOnly: Boolean
# User credentials
@@ -536,12 +539,10 @@ input ConnectionConfig {
selectedSecretId: ID @since(version: "23.3.5")
credentials: Object
# Map of provider properties (name/value)
# Return map of provider properties (name/value)
providerProperties: Object
# Network handlers. Map of id->property map (name/value).
# Return network handlers configuration. Map of id->property map (name/value).
networkHandlersConfig: [NetworkHandlerConfigInput!]
#### deprecated fields
@@ -565,15 +566,16 @@ input ConnectionConfig {
####################################################
extend type Query {
# Returns server config
# Return server config
serverConfig: ServerConfig!
# Return product settings
productSettings: ProductSettings! @since(version: "24.0.1")
# Returns session state ( initialize if not )
# Return session state ( initialize if not )
sessionState: SessionInfo!
# Session permissions
# Return session permissions
sessionPermissions: [ID]!
# Get driver info
@@ -581,9 +583,10 @@ extend type Query {
authModels: [DatabaseAuthModel!]!
networkHandlers: [NetworkHandlerDescriptor!]!
# List of user connections.
# Return list of user connections
userConnections( projectId: ID, id: ID, projectIds: [ID!] ): [ ConnectionInfo! ]!
# List of template connections.
# Return list of template connections by project ID
templateConnections( projectId: ID ): [ ConnectionInfo! ]!
# List of connection folders
@@ -613,21 +616,25 @@ extend type Mutation {
# Refresh session connection list
refreshSessionConnections: Boolean
# Refreshes session on server and returns its state
# Change session language to specified
changeSessionLanguage(locale: String): Boolean
# Create new custom connection. Custom connections exist only within the current session.
# Create new custom connection
createConnection( config: ConnectionConfig!, projectId: ID ): ConnectionInfo!
# Update specified connection
updateConnection( config: ConnectionConfig!, projectId: ID ): ConnectionInfo!
# Delete specified connection
deleteConnection( id: ID!, projectId: ID ): Boolean!
# Create new custom connection from template
createConnectionFromTemplate( templateId: ID!, projectId: ID!, connectionName: String ): ConnectionInfo!
# Create new folder
# Create new folder for connections
createConnectionFolder(parentFolderPath: ID, folderName: String!, projectId: ID ): ConnectionFolderInfo!
# Delete specified connection folder
deleteConnectionFolder( folderPath: ID!, projectId: ID ): Boolean!
# Copies connection configuration from node
@@ -636,7 +643,7 @@ extend type Mutation {
# Test connection configuration. Returns remote server version
testConnection( config: ConnectionConfig!, projectId: ID): ConnectionInfo!
# Test connection configuration. Returns remote server version
# Test network handler
testNetworkHandler( config: NetworkHandlerConfigInput! ): NetworkEndpointInfo!
# Initiate existing connection
@@ -646,7 +653,7 @@ extend type Mutation {
# Disconnect from database
closeConnection( id: ID!, projectId: ID ): ConnectionInfo!
# Changes navigator settings for connection
# Change navigator settings for connection
setConnectionNavigatorSettings( id: ID!, projectId: ID, settings: NavigatorSettingsInput!): ConnectionInfo!
#### Generic async functions
@@ -335,8 +335,8 @@ public class WebServiceUtils extends WebCommonUtils {
return container.getName() + " [" + container.getId() + "]";
}
public static void updateConfigAndRefreshDatabases(WebSession session, String projectId) {
DBNProject projectNode = session.getNavigatorModel().getRoot().getProjectNode(session.getProjectById(projectId));
public static void updateConfigAndRefreshDatabases(WebSession session, String projectId) throws DBWebException {
DBNProject projectNode = session.getNavigatorModelOrThrow().getRoot().getProjectNode(session.getProjectById(projectId));
DBNModel.updateConfigAndRefreshDatabases(projectNode.getDatabases());
}
@@ -20,6 +20,7 @@ import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebSession;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.navigator.DBNModel;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDatasourceFolderEvent;
import org.jkiss.utils.CommonUtils;
@@ -32,15 +33,19 @@ public class WSFolderUpdatedEventHandlerImpl extends WSAbstractProjectEventHandl
@Override
protected void updateSessionData(@NotNull BaseWebSession activeUserSession, @NotNull WSDatasourceFolderEvent event) {
if (activeUserSession instanceof WebSession) {
var webSession = (WebSession) activeUserSession;
if (activeUserSession instanceof WebSession webSession) {
var project = webSession.getProjectById(event.getProjectId());
if (project == null) {
log.debug("Project " + event.getProjectId() + " is not found in session " + webSession.getSessionId());
return;
}
project.getDataSourceRegistry().refreshConfig();
webSession.getNavigatorModel().getRoot().getProjectNode(project).getDatabases().refreshChildren();
DBNModel navigatorModel = webSession.getNavigatorModel();
if (navigatorModel == null) {
log.debug("Navigator model is not found in session " + webSession.getSessionId());
return;
}
navigatorModel.getRoot().getProjectNode(project).getDatabases().refreshChildren();
}
activeUserSession.addSessionEvent(event);
}
@@ -42,6 +42,7 @@ import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.auth.SMAuthInfo;
import org.jkiss.dbeaver.model.auth.SMAuthProvider;
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
import org.jkiss.dbeaver.utils.MimeTypes;
import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.IOUtils;
@@ -225,6 +226,7 @@ public class CBStaticServlet extends DefaultServlet {
// Disable cache for index.html
response.setHeader(HttpHeader.CACHE_CONTROL.toString(), "no-cache, no-store, must-revalidate");
response.setHeader(HttpHeader.CONTENT_TYPE.toString(), MimeTypes.TEXT_HTML);
response.setHeader(HttpHeader.EXPIRES.toString(), "0");
response.getOutputStream().write(ByteBuffer.wrap(indexBytes));
}
@@ -639,7 +639,7 @@ public class WebServiceCore implements DBWServiceCore {
@NotNull WebConnectionConfig config
) throws DBWebException {
try {
DBNModel navigatorModel = webSession.getNavigatorModel();
DBNModel navigatorModel = webSession.getNavigatorModelOrThrow();
WebSessionProjectImpl project = getProjectById(webSession, projectId);
DBPDataSourceRegistry dataSourceRegistry = project.getDataSourceRegistry();
@@ -324,8 +324,14 @@ public class WebNavigatorNodeInfo {
if (!(node instanceof DBNDatabaseNode dbNode)) {
throw new DBWebException("Invalid navigator node type: " + node.getClass().getName());
}
DBSObjectFilter filter = dbNode.getNodeFilter(dbNode.getItemsMeta(), true);
return filter == null || filter.isEmpty() || !filter.isEnabled() ? null : filter;
try {
DBSObjectFilter filter = dbNode.getNodeFilter(
DBNUtils.getValidItemsMeta(session.getProgressMonitor(), dbNode),
true);
return filter == null || filter.isEmpty() || !filter.isEnabled() ? null : filter;
} catch (DBException e) {
throw new DBWebException(e);
}
}
@Override
@@ -19,7 +19,6 @@ package io.cloudbeaver.service.navigator.impl;
import io.cloudbeaver.BaseWebProjectImpl;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.WebProjectImpl;
import io.cloudbeaver.WebServiceUtils;
import io.cloudbeaver.model.WebCommandContext;
import io.cloudbeaver.model.WebConnectionInfo;
@@ -43,6 +42,8 @@ import org.jkiss.dbeaver.model.edit.DBEObjectRenamer;
import org.jkiss.dbeaver.model.exec.DBCExecutionContext;
import org.jkiss.dbeaver.model.exec.DBCExecutionContextDefaults;
import org.jkiss.dbeaver.model.navigator.*;
import org.jkiss.dbeaver.model.navigator.meta.DBXTreeItem;
import org.jkiss.dbeaver.model.rm.RMControllerProvider;
import org.jkiss.dbeaver.model.rm.RMProject;
import org.jkiss.dbeaver.model.rm.RMProjectPermission;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
@@ -82,7 +83,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
DBNNode[] nodeChildren;
boolean isRootPath = CommonUtils.isEmpty(parentPath) || "/".equals(parentPath) || ROOT_DATABASES.equals(parentPath);
DBNModel navigatorModel = session.getNavigatorModel();
DBNModel navigatorModel = session.getNavigatorModelOrThrow();
Set<String> applicableDrivers = WebServiceUtils.getApplicableDriversIds();
if (isRootPath) {
DBNRoot rootNode = navigatorModel.getRoot();
@@ -142,7 +143,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
return result.subList(offset, result.size());
}
} catch (DBException e) {
throw new DBWebException(e);
throw new DBWebException(e.getMessage(), e);
}
}
@@ -154,8 +155,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
try {
DBRProgressMonitor monitor = session.getProgressMonitor();
DBNModel navigatorModel = session.getNavigatorModel();
DBNNode node = navigatorModel.getNodeByPath(monitor, nodePath);
DBNNode node = session.getNavigatorModelOrThrow().getNodeByPath(monitor, nodePath);
if (node == null) {
throw new DBWebException("Node '" + nodePath + "' not found");
}
@@ -197,7 +197,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
try {
DBRProgressMonitor monitor = session.getProgressMonitor();
DBNNode node = session.getNavigatorModel().getNodeByPath(monitor, nodePath);
DBNNode node = session.getNavigatorModelOrThrow().getNodeByPath(monitor, nodePath);
if (node == null) {
throw new DBWebException("Navigator node '" + nodePath + "' not found");
}
@@ -216,7 +216,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
try {
DBRProgressMonitor monitor = webSession.getProgressMonitor();
DBNNode node = webSession.getNavigatorModel().getNodeByPath(monitor, nodePath);
DBNNode node = webSession.getNavigatorModelOrThrow().getNodeByPath(monitor, nodePath);
if (node == null) {
throw new DBWebException("Navigator node '" + nodePath + "' not found");
}
@@ -229,8 +229,11 @@ public class WebServiceNavigator implements DBWServiceNavigator {
}
filter.setEnabled(true);
if (node instanceof DBNDatabaseNode dbNode) {
dbNode.setNodeFilter(dbNode.getItemsMeta(), filter, true);
if (hasNodeEditPermission(webSession, node, ((WebProjectImpl) node.getOwnerProject()).getRMProject())) {
DBXTreeItem itemsMeta = DBNUtils.getValidItemsMeta(webSession.getProgressMonitor(), dbNode);
dbNode.setNodeFilter(itemsMeta, filter, true);
if (node.getOwnerProject() instanceof RMControllerProvider rmControllerProvider &&
hasNodeEditPermission(webSession, node, rmControllerProvider.getRMProject())
) {
// Save settings
dbNode.getDataSourceContainer().persistConfiguration();
}
@@ -252,7 +255,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
try {
DBRProgressMonitor monitor = session.getProgressMonitor();
DBNNode node = session.getNavigatorModel().getNodeByPath(monitor, nodePath);
DBNNode node = session.getNavigatorModelOrThrow().getNodeByPath(monitor, nodePath);
if (node == null) {
throw new DBWebException("Navigator node '" + nodePath + "' not found");
}
@@ -376,10 +379,9 @@ public class WebServiceNavigator implements DBWServiceNavigator {
}
@Nullable
protected WebNavigatorNodeInfo getNodeFromObject(WebSession session, DBSObject object){
DBNModel navigatorModel = session.getNavigatorModel();
protected WebNavigatorNodeInfo getNodeFromObject(WebSession session, DBSObject object) throws DBWebException {
DBRProgressMonitor monitor = session.getProgressMonitor();
DBNNode node = navigatorModel.getNodeByObject(monitor, object, false);
DBNNode node = session.getNavigatorModelOrThrow().getNodeByObject(monitor, object, false);
return node == null ? null : new WebNavigatorNodeInfo(session, node);
}
@@ -393,7 +395,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
try {
DBRProgressMonitor monitor = session.getProgressMonitor();
DBNNode node = session.getNavigatorModel().getNodeByPath(monitor, nodePath);
DBNNode node = session.getNavigatorModelOrThrow().getNodeByPath(monitor, nodePath);
if (node == null) {
throw new DBWebException("Navigator node '" + nodePath + "' not found");
}
@@ -498,8 +500,9 @@ public class WebServiceNavigator implements DBWServiceNavigator {
String projectId = null;
boolean containsFolderNodes = false;
Map<DBNNode, DBEObjectMaker> nodes = new LinkedHashMap<>();
DBNModel model = session.getNavigatorModelOrThrow();
for (String path : nodePaths) {
DBNNode node = session.getNavigatorModel().getNodeByPath(monitor, path);
DBNNode node = model.getNodeByPath(monitor, path);
if (node == null) {
throw new DBWebException("Navigator node '" + path + "' not found");
}
@@ -594,9 +597,10 @@ public class WebServiceNavigator implements DBWServiceNavigator {
try {
DBRProgressMonitor monitor = session.getProgressMonitor();
DBNNode folderNode;
folderNode = session.getNavigatorModel().getNodeByPath(monitor, folderNodePath);
DBNModel navigatorModel = session.getNavigatorModelOrThrow();
folderNode = navigatorModel.getNodeByPath(monitor, folderNodePath);
for (String path : nodePaths) {
DBNNode node = session.getNavigatorModel().getNodeByPath(monitor, path);
DBNNode node = navigatorModel.getNodeByPath(monitor, path);
if (node == null) {
throw new DBWebException("Navigator node '" + path + "' not found");
}
@@ -123,6 +123,6 @@ public class WebSQLCompletionContext implements SQLCompletionContext {
@Override
public SQLCompletionProposalBase createProposal(@NotNull SQLCompletionRequest request, @NotNull String displayString, @NotNull String replacementString, int cursorPosition, @Nullable DBPImage image, @NotNull DBPKeywordType proposalType, @Nullable String description, @Nullable DBPNamedObject object, @NotNull Map<String, Object> params) {
return new SQLCompletionProposalBase(this, request.getWordDetector(), displayString, replacementString, cursorPosition, image, proposalType, description, object, params);
return new SQLCompletionProposalBase(request, displayString, replacementString, cursorPosition, image, proposalType, description, object, params);
}
}
@@ -950,7 +950,7 @@ public class WebSQLProcessor implements WebSessionProvider {
@NotNull
public <T> T getDataContainerByNodePath(DBRProgressMonitor monitor, @NotNull String containerPath, Class<T> type) throws DBException {
DBNNode node = webSession.getNavigatorModel().getNodeByPath(monitor, containerPath);
DBNNode node = webSession.getNavigatorModelOrThrow().getNodeByPath(monitor, containerPath);
if (node == null) {
throw new DBWebException("Container node '" + containerPath + "' not found");
}
@@ -35,10 +35,7 @@ import org.jkiss.dbeaver.model.struct.DBSEntity;
import org.jkiss.utils.CommonUtils;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
class WebSQLQueryDataReceiver implements DBDDataReceiver {
@@ -170,8 +167,9 @@ class WebSQLQueryDataReceiver implements DBDDataReceiver {
webResultSet.setSingleEntity(isSingleEntity);
DBDRowIdentifier rowIdentifier = resultsInfo.getDefaultRowIdentifier();
webResultSet.setHasRowIdentifier(rowIdentifier != null && rowIdentifier.isValidIdentifier());
Set<DBDRowIdentifier> rowIdentifiers = resultsInfo.getRowIdentifiers();
boolean hasRowIdentifier = rowIdentifiers.stream().allMatch(DBDRowIdentifier::isValidIdentifier);
webResultSet.setHasRowIdentifier(!rowIdentifiers.isEmpty() && hasRowIdentifier);
}
private void convertComplexValuesToRelationalView(DBCSession session) {
@@ -40,6 +40,7 @@ import org.jkiss.dbeaver.model.exec.trace.DBCTrace;
import org.jkiss.dbeaver.model.exec.trace.DBCTraceDynamic;
import org.jkiss.dbeaver.model.exec.trace.DBCTraceProperty;
import org.jkiss.dbeaver.model.impl.sql.BasicSQLDialect;
import org.jkiss.dbeaver.model.navigator.DBNModel;
import org.jkiss.dbeaver.model.navigator.DBNNode;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.sql.*;
@@ -241,8 +242,9 @@ public class WebServiceSQL implements DBWServiceSQL {
private List<DBSObject> getObjectListFromNodeIds(@NotNull WebSession session, @NotNull List<String> nodePathList) throws DBWebException {
try {
List<DBSObject> objectList = new ArrayList<>(nodePathList.size());
DBNModel navigatorModel = session.getNavigatorModelOrThrow();
for (String nodePath : nodePathList) {
DBNNode node = session.getNavigatorModel().getNodeByPath(session.getProgressMonitor(), nodePath);
DBNNode node = navigatorModel.getNodeByPath(session.getProgressMonitor(), nodePath);
if (node == null) {
throw new DBException("Node '" + nodePath + "' not found");
}
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - Administration
Bundle-SymbolicName: io.cloudbeaver.service.admin;singleton:=true
Bundle-Version: 1.0.107.qualifier
Bundle-Release-Date: 20241021
Bundle-Version: 1.0.108.qualifier
Bundle-Release-Date: 20241104
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.admin</artifactId>
<version>1.0.107-SNAPSHOT</version>
<version>1.0.108-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - Authentication
Bundle-SymbolicName: io.cloudbeaver.service.auth;singleton:=true
Bundle-Version: 1.0.107.qualifier
Bundle-Release-Date: 20241021
Bundle-Version: 1.0.108.qualifier
Bundle-Release-Date: 20241104
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.auth</artifactId>
<version>1.0.107-SNAPSHOT</version>
<version>1.0.108-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -137,6 +137,9 @@ type UserInfo {
configurationParameters: Object!
# User teams
teams: [UserTeamInfo!]!
@since(version: "24.2.3")
isAnonymous: Boolean!
}
type UserTeamInfo {
@@ -69,6 +69,9 @@ public class WebUserInfo {
@Property
public List<String> getLinkedAuthProviders() throws DBWebException {
if (isAnonymous()) {
return List.of();
}
if (linkedProviders == null) {
try {
linkedProviders = session.getSecurityController().getCurrentUserLinkedProviders();
@@ -104,4 +107,9 @@ public class WebUserInfo {
return List.of();
}
}
@Property
public boolean isAnonymous() {
return session.getUser() == null;
}
}
@@ -21,6 +21,7 @@ import io.cloudbeaver.WebServiceUtils;
import io.cloudbeaver.auth.SMSignOutLinkProvider;
import io.cloudbeaver.auth.provider.local.LocalAuthProvider;
import io.cloudbeaver.model.WebPropertyInfo;
import io.cloudbeaver.model.app.WebAppConfiguration;
import io.cloudbeaver.model.session.WebAuthInfo;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.model.session.WebSessionAuthProcessor;
@@ -189,7 +190,12 @@ public class WebServiceAuthImpl implements DBWServiceAuth {
@Override
public WebUserInfo activeUser(@NotNull WebSession webSession) throws DBWebException {
if (webSession.getUser() == null) {
return null;
WebAppConfiguration appConfiguration = webSession.getApplication().getAppConfiguration();
if (!appConfiguration.isAnonymousAccessEnabled()) {
return null;
}
SMUser anonymous = new SMUser("anonymous", true, null);
return new WebUserInfo(webSession, new WebUser(anonymous));
}
try {
// Read user from security controller. It will also read meta parameters
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - Data Transfer
Bundle-SymbolicName: io.cloudbeaver.service.data.transfer;singleton:=true
Bundle-Version: 1.0.108.qualifier
Bundle-Release-Date: 20241021
Bundle-Version: 1.0.109.qualifier
Bundle-Release-Date: 20241104
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.data.transfer</artifactId>
<version>1.0.108-SNAPSHOT</version>
<version>1.0.109-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - File System
Bundle-SymbolicName: io.cloudbeaver.service.fs;singleton:=true
Bundle-Version: 1.0.25.qualifier
Bundle-Release-Date: 20241021
Bundle-Version: 1.0.26.qualifier
Bundle-Release-Date: 20241104
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.fs</artifactId>
<version>1.0.25-SNAPSHOT</version>
<version>1.0.26-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -51,7 +51,7 @@ public class WebServiceFS implements DBWServiceFS {
if (project == null) {
throw new DBException(MessageFormat.format("Project ''{0}'' is not found in session", projectId));
}
DBNProject projectNode = webSession.getNavigatorModel().getRoot().getProjectNode(project);
DBNProject projectNode = webSession.getNavigatorModelOrThrow().getRoot().getProjectNode(project);
if (projectNode == null) {
throw new DBException(MessageFormat.format("Project ''{0}'' is not found in navigator model", projectId));
}
@@ -78,7 +78,7 @@ public class WebServiceFS implements DBWServiceFS {
@NotNull String nodePath
) throws DBWebException {
try {
var node = webSession.getNavigatorModel().getNodeByPath(webSession.getProgressMonitor(), nodePath);
var node = webSession.getNavigatorModelOrThrow().getNodeByPath(webSession.getProgressMonitor(), nodePath);
if (!(node instanceof DBNFileSystem fs)) {
throw new DBException(MessageFormat.format("Node ''{0}'' is not File System", nodePath));
}
@@ -1,6 +1,6 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Vendor: Cloudbeaver LDAP
Bundle-Name: Cloudbeaver Web Service - LDAP
Bundle-Vendor: DBeaver Corp
Bundle-SymbolicName: io.cloudbeaver.service.ldap.auth;singleton:=true
Bundle-Version: 1.0.0.qualifier
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - Metadata
Bundle-SymbolicName: io.cloudbeaver.service.metadata;singleton:=true
Bundle-Version: 1.0.111.qualifier
Bundle-Release-Date: 20241021
Bundle-Version: 1.0.112.qualifier
Bundle-Release-Date: 20241104
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.metadata</artifactId>
<version>1.0.111-SNAPSHOT</version>
<version>1.0.112-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -52,6 +52,9 @@ public class WebServiceBindingMetadata extends WebServiceBindingBase<DBWServiceM
private DBNNode getNodeFromPath(DataFetchingEnvironment env) throws DBException {
WebSession webSession = getWebSession(env);
String nodePath = env.getArgument("nodeId");
return webSession.getNavigatorModel().getNodeByPath(webSession.getProgressMonitor(), nodePath);
if (nodePath == null) {
throw new DBException("Node path is null");
}
return webSession.getNavigatorModelOrThrow().getNodeByPath(webSession.getProgressMonitor(), nodePath);
}
}
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Resource manager NIO implementation
Bundle-SymbolicName: io.cloudbeaver.service.rm.nio;singleton:=true
Bundle-Version: 1.0.25.qualifier
Bundle-Release-Date: 20241021
Bundle-Version: 1.0.26.qualifier
Bundle-Release-Date: 20241104
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.rm.nio</artifactId>
<version>1.0.25-SNAPSHOT</version>
<version>1.0.26-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - Resource manager
Bundle-SymbolicName: io.cloudbeaver.service.rm;singleton:=true
Bundle-Version: 1.0.60.qualifier
Bundle-Release-Date: 20241021
Bundle-Version: 1.0.61.qualifier
Bundle-Release-Date: 20241104
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.rm</artifactId>
<version>1.0.60-SNAPSHOT</version>
<version>1.0.61-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -1,10 +1,10 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Vendor: Cloudbeaver Web Service - Security
Bundle-Name: Cloudbeaver Web Service - Security
Bundle-Vendor: DBeaver Corp
Bundle-SymbolicName: io.cloudbeaver.service.security;singleton:=true
Bundle-Version: 1.0.63.qualifier
Bundle-Release-Date: 20241021
Bundle-Version: 1.0.64.qualifier
Bundle-Release-Date: 20241104
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.security</artifactId>
<version>1.0.63-SNAPSHOT</version>
<version>1.0.64-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -838,7 +838,7 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
@NotNull String authProviderId,
@NotNull Map<String, Object> credentials
) throws DBException {
var existUserByCredentials = findUserByCredentials(getAuthProvider(authProviderId), credentials);
var existUserByCredentials = findUserByCredentials(getAuthProvider(authProviderId), credentials, false);
if (existUserByCredentials != null && !existUserByCredentials.equals(userId)) {
throw new DBException("Another user is already linked to the specified credentials");
}
@@ -906,7 +906,11 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
}
@Nullable
private String findUserByCredentials(WebAuthProviderDescriptor authProvider, Map<String, Object> authParameters) throws DBCException {
private String findUserByCredentials(
@NotNull WebAuthProviderDescriptor authProvider,
@NotNull Map<String, Object> authParameters,
boolean onlyActive // throws exception if user is inactive
) throws DBCException {
Map<String, Object> identCredentials = new LinkedHashMap<>();
String[] propNames = authParameters.keySet().toArray(new String[0]);
for (AuthPropertyDescriptor prop : authProvider.getCredentialParameters(propNames)) {
@@ -961,7 +965,7 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
}
}
if (userId != null && !isActive) {
if (userId != null && onlyActive && !isActive) {
throw new DBCException("User account is locked");
}
@@ -2405,7 +2409,7 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
) throws DBException {
SMAuthProvider<?> smAuthProviderInstance = authProvider.getInstance();
String userId = findUserByCredentials(authProvider, userCredentials);
String userId = findUserByCredentials(authProvider, userCredentials, true);
String userIdFromCredentials;
try {
userIdFromCredentials = smAuthProviderInstance.validateLocalAuth(progressMonitor, this, providerConfig, userCredentials, null);
@@ -3134,6 +3138,7 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
}
}
@NotNull
protected WebAuthProviderDescriptor getAuthProvider(String authProviderId) throws DBCException {
WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(authProviderId);
if (authProvider == null) {
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: CloudBeaver SLF4j Binding
Bundle-SymbolicName: io.cloudbeaver.slf4j;singleton:=true
Bundle-Version: 1.0.23.qualifier
Bundle-Release-Date: 20241021
Bundle-Version: 1.0.24.qualifier
Bundle-Release-Date: 20241104
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
+1 -1
View File
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.slf4j</artifactId>
<version>1.0.23-SNAPSHOT</version>
<version>1.0.24-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -2,7 +2,7 @@
<feature
id="io.cloudbeaver.ce.drivers.feature"
label="Cloudbeaver CE 3rd Party Drivers"
version="1.0.131.qualifier"
version="1.0.132.qualifier"
provider-name="DBeaver Corp">
<plugin id="io.cloudbeaver.resources.drivers.base" download-size="0" install-size="0" version="0.0.0"/>
@@ -9,6 +9,6 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.ce.drivers.feature</artifactId>
<version>1.0.131-SNAPSHOT</version>
<version>1.0.132-SNAPSHOT</version>
<packaging>eclipse-feature</packaging>
</project>
@@ -2,7 +2,7 @@
<feature
id="io.cloudbeaver.product.ce.feature"
label="%featureName"
version="24.2.3.qualifier"
version="24.2.4.qualifier"
provider-name="%providerName"
plugin="io.cloudbeaver.product.ce">
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.product.ce.feature</artifactId>
<version>24.2.3-SNAPSHOT</version>
<version>24.2.4-SNAPSHOT</version>
<packaging>eclipse-feature</packaging>
<build>
@@ -2,7 +2,7 @@
<feature
id="io.cloudbeaver.server.feature"
label="%featureName"
version="24.2.3.qualifier"
version="24.2.4.qualifier"
provider-name="%providerName"
plugin="io.cloudbeaver.server">
@@ -10,6 +10,6 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.server.feature</artifactId>
<version>24.2.3-SNAPSHOT</version>
<version>24.2.4-SNAPSHOT</version>
<packaging>eclipse-feature</packaging>
</project>
@@ -2,7 +2,7 @@
<feature
id="io.cloudbeaver.ws.feature"
label="%featureName"
version="1.0.61.qualifier"
version="1.0.62.qualifier"
provider-name="%providerName"
plugin="io.cloudbeaver.model">
@@ -10,6 +10,6 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.ws.feature</artifactId>
<version>1.0.61-SNAPSHOT</version>
<version>1.0.62-SNAPSHOT</version>
<packaging>eclipse-feature</packaging>
</project>
+1 -1
View File
@@ -19,7 +19,7 @@
<properties>
<dbeaver-product>CloudBeaver CE</dbeaver-product>
<dbeaver-version>24.2.3</dbeaver-version>
<dbeaver-version>24.2.4</dbeaver-version>
</properties>
<modules>
@@ -2,7 +2,7 @@
<?pde version="3.5"?>
<product name="CloudBeaver ce Server" uid="io.cloudbeaver.product" id="io.cloudbeaver.product.ce.product"
application="io.cloudbeaver.product.ce.application" version="24.2.3.qualifier" useFeatures="true"
application="io.cloudbeaver.product.ce.application" version="24.2.4.qualifier" useFeatures="true"
includeLaunchers="false">
<configIni use="default">
+1 -1
View File
@@ -9,7 +9,7 @@
<version>1.0.0-SNAPSHOT</version>
<relativePath>../../</relativePath>
</parent>
<version>24.2.3-SNAPSHOT</version>
<version>24.2.4-SNAPSHOT</version>
<artifactId>web-server</artifactId>
<packaging>eclipse-repository</packaging>
<name>Cloudbeaver Server Product</name>
@@ -5,6 +5,8 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import type { ILoadableState } from '@cloudbeaver/core-utils';
import type { IRouteParams } from './IRouteParams.js';
export enum AdministrationItemType {
@@ -84,6 +86,7 @@ export interface IAdministrationItemOptions {
replace?: IAdministrationItemReplaceOptions;
defaultSub?: string;
defaultParam?: string;
getLoader?: () => ILoadableState[] | ILoadableState;
getDrawerComponent: () => AdministrationItemDrawerComponent;
getContentComponent: () => AdministrationItemContentComponent;
onLoad?: AdministrationItemEvent;
@@ -16,9 +16,7 @@ import { UserInfoResource } from './UserInfoResource.js';
@injectable()
export class AppAuthService extends Bootstrap {
get authenticated(): boolean {
const user = this.userInfoResource.data;
return this.serverConfigResource.anonymousAccessEnabled || this.serverConfigResource.configurationMode || user !== null;
return this.serverConfigResource.configurationMode || this.userInfoResource.hasAccess();
}
get loaders(): ILoadableState[] {
@@ -56,9 +54,9 @@ export class AppAuthService extends Bootstrap {
throw new Error("Can't configure Authentication");
}
const user = await this.userInfoResource.load();
await this.userInfoResource.load();
return !this.serverConfigResource.configurationMode && !this.serverConfigResource.anonymousAccessEnabled && user === null;
return !this.serverConfigResource.configurationMode && !this.userInfoResource.hasAccess();
}
async authUser(): Promise<boolean> {
@@ -25,10 +25,6 @@ export class AuthInfoService {
return this.userInfoResource.data;
}
get isAnonymous(): boolean {
return !this.userInfoResource.data;
}
constructor(
private readonly userInfoResource: UserInfoResource,
private readonly authProvidersResource: AuthProvidersResource,
@@ -76,6 +76,18 @@ export class UserInfoResource extends CachedDataResource<UserInfo | null, void,
});
}
isAnonymous(): this is { data: UserInfo } {
return this.data?.isAnonymous === true;
}
isAuthenticated(): this is { data: UserInfo } {
return !!this.data && !this.isAnonymous();
}
hasAccess(): this is { data: UserInfo } {
return this.isAnonymous() || this.isAuthenticated();
}
isLinked(provideId: string): boolean {
return this.data?.linkedAuthProviders.includes(provideId) || false;
}
@@ -5,7 +5,6 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
.box {
height: 600px;
.button {
transform: rotate(90deg);
}
@@ -0,0 +1,25 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 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 { ActionIconButton } from '../ActionIconButton.js';
import { Flex } from '../Flex/Flex.js';
import { useTranslate } from '../localization/useTranslate.js';
import { s } from '../s.js';
import { useS } from '../useS.js';
import classes from './GroupBack.module.css';
export const GroupBack: React.FC<React.HTMLAttributes<HTMLButtonElement>> = function GroupBack({ className, children, ...rest }) {
const translate = useTranslate();
const styles = useS(classes);
return (
<Flex gap="xs" align="center">
<ActionIconButton className={s(styles, { button: true }, className)} name="angle" title={translate('ui_close')} {...rest} />
{children}
</Flex>
);
};
@@ -26,5 +26,6 @@ export const GroupTitle: React.FC<Props & ILayoutSizeProps & React.HTMLAttribute
const style = useS(styles, elementsSizeStyles);
const divProps = filterLayoutFakeProps(rest);
const layoutProps = getLayoutProps(rest);
return <h2 tabIndex={-1} {...divProps} className={s(style, { groupTitle: true, sticky, header, ...layoutProps }, className)} />;
};
@@ -11,6 +11,7 @@
box-sizing: border-box;
padding-top: 4px;
min-height: 24px;
word-break: break-word;
&.invalid {
color: var(--theme-negative);
@@ -10,6 +10,12 @@
display: inline;
}
&.truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
& a {
position: relative;
cursor: pointer;
+3 -2
View File
@@ -16,14 +16,15 @@ interface Props extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
wrapper?: boolean;
indicator?: boolean;
inline?: boolean;
truncate?: boolean;
children?: React.ReactNode;
}
export const Link = observer<Props>(function Link({ inline, wrapper, indicator, className, children, ...rest }) {
export const Link = observer<Props>(function Link({ inline, wrapper, indicator, truncate, className, children, ...rest }) {
const styles = useS(style);
return (
<div className={s(styles, { linkContainer: true, inline }, className)}>
<div className={s(styles, { linkContainer: true, inline, truncate }, className)}>
<a className={s(styles, { link: true, wrapper })} {...rest}>
{indicator && <IconOrImage className={s(styles, { iconOrImage: true })} icon="external-link" />}
{children}
@@ -56,6 +56,11 @@
display: flex;
}
.remove,
.select {
margin: 0 2px;
}
.container:hover .remove {
opacity: 1;
}
@@ -106,7 +111,6 @@
.button,
.propertyValueSelector {
composes: theme-form-element-radius from global;
margin: 2px;
overflow: hidden;
}
@@ -13,10 +13,12 @@
white-space: normal;
transition: transform ease-in-out 0.3s;
transform: translateX(-100%);
will-change: transform;
&:first-child {
transition: width ease-in-out 0.3s;
width: 100%;
will-change: width;
}
}
@@ -0,0 +1,12 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
.truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
+15 -2
View File
@@ -7,6 +7,19 @@
*/
import { observer } from 'mobx-react-lite';
export const Text: React.FC<React.HTMLAttributes<HTMLDivElement>> = observer(function Text({ children, ...rest }) {
return <div {...rest}>{children}</div>;
import { s } from './s.js';
import classes from './Text.module.css';
import { useS } from './useS.js';
interface Props extends React.HTMLAttributes<HTMLDivElement> {
truncate?: boolean;
}
export const Text = observer<Props>(function Text({ truncate, children, className, ...rest }) {
const styles = useS(classes);
return (
<div className={s(styles, { truncate }, className)} {...rest}>
{children}
</div>
);
});
+1 -1
View File
@@ -161,6 +161,7 @@ export * from './Containers/GroupClose.js';
export * from './Containers/GroupItem.js';
export * from './Containers/GroupSubTitle.js';
export * from './Containers/GroupTitle.js';
export * from './Containers/GroupBack.js';
export * from './Containers/ColoredContainer.js';
export * from './Containers/IContainerProps.js';
export * from './Containers/ILayoutSizeProps.js';
@@ -219,7 +220,6 @@ export * from './useClipboard.js';
export * from './useCombinedHandler.js';
export * from './useCombinedRef.js';
export * from './useExecutor.js';
export * from './useFn.js';
export * from './useFocus.js';
export * from './useFormValidator.js';
export * from './ResourcesHooks/useOffsetPagination.js';
@@ -0,0 +1,159 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 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 { afterEach, beforeEach, describe, expect, it, jest } from '@jest/globals';
import { act, renderHook } from '@testing-library/react';
import { useActivationDelay } from './useActivationDelay.js';
describe('useActivationDelay', () => {
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.clearAllTimers();
jest.useRealTimers();
});
it('should initially return false', () => {
const { result } = renderHook(() => useActivationDelay(false, 1000));
expect(result.current).toBe(false);
});
it('should not change state before delay when activated', () => {
const { result } = renderHook(() => useActivationDelay(true, 1000));
expect(result.current).toBe(false);
act(() => {
jest.advanceTimersByTime(500);
});
expect(result.current).toBe(false);
});
it('should change state after delay when activated', () => {
const { result } = renderHook(() => useActivationDelay(true, 1000));
act(() => {
jest.advanceTimersByTime(1000);
});
expect(result.current).toBe(true);
});
it('should fire callback after delay when activated', () => {
const callback = jest.fn();
renderHook(() => useActivationDelay(true, 1000, callback));
expect(callback).not.toHaveBeenCalled();
act(() => {
jest.advanceTimersByTime(1000);
});
expect(callback).toHaveBeenCalledTimes(1);
});
it('should handle callback changes', () => {
const initialCallback = jest.fn();
const newCallback = jest.fn();
const { rerender } = renderHook(({ callback }) => useActivationDelay(true, 1000, callback), { initialProps: { callback: initialCallback } });
rerender({ callback: newCallback });
act(() => {
jest.advanceTimersByTime(1000);
});
expect(initialCallback).not.toHaveBeenCalled();
expect(newCallback).toHaveBeenCalledTimes(1);
});
it('should clear timeout on unmount', () => {
const callback = jest.fn();
const { unmount } = renderHook(() => useActivationDelay(true, 1000, callback));
unmount();
act(() => {
jest.advanceTimersByTime(1000);
});
expect(callback).not.toHaveBeenCalled();
});
it('should not fire callback if delay changed', () => {
const callback = jest.fn();
const { rerender } = renderHook(({ delay }) => useActivationDelay(true, delay, callback), { initialProps: { delay: 1000 } });
act(() => {
jest.advanceTimersByTime(500);
});
rerender({ delay: 2000 });
act(() => {
jest.advanceTimersByTime(1500);
});
expect(callback).toHaveBeenCalledTimes(0);
});
it('should not fire callback when state changed to false', () => {
const callback = jest.fn();
const { rerender } = renderHook(({ state }) => useActivationDelay(state, 1000, callback), { initialProps: { state: true } });
act(() => {
jest.advanceTimersByTime(500);
});
rerender({ state: false });
act(() => {
jest.advanceTimersByTime(500);
});
expect(callback).toHaveBeenCalledTimes(0);
});
it('should handle rapid state changes', () => {
const callback = jest.fn();
const { rerender } = renderHook(({ state }) => useActivationDelay(state, 1000, callback), { initialProps: { state: true } });
act(() => {
jest.advanceTimersByTime(500);
});
rerender({ state: false });
act(() => {
jest.advanceTimersByTime(100);
});
rerender({ state: true });
act(() => {
jest.advanceTimersByTime(1000);
});
expect(callback).toHaveBeenCalledTimes(1);
});
it('should work with zero delay', () => {
const callback = jest.fn();
renderHook(() => useActivationDelay(true, 0, callback));
act(() => {
jest.advanceTimersByTime(0);
});
expect(callback).toHaveBeenCalledTimes(1);
});
});
@@ -0,0 +1,69 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 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 { describe, expect, jest, test } from '@jest/globals';
import { renderHook } from '@testing-library/react';
import { useCombinedHandler } from './useCombinedHandler.js';
jest.mock('./useObjectRef', () => ({
useObjectRef: jest.fn(value => value),
}));
describe('useCombinedHandler', () => {
test('should call all provided handlers', () => {
const handler1 = jest.fn();
const handler2 = jest.fn();
const { result } = renderHook(() => useCombinedHandler(handler1, handler2));
result.current('arg1', 'arg2');
expect(handler1).toHaveBeenCalledWith('arg1', 'arg2');
expect(handler2).toHaveBeenCalledWith('arg1', 'arg2');
});
test('should handle null and undefined handlers', () => {
const { result } = renderHook(() => useCombinedHandler(null, undefined));
expect(() => result.current('testArg')).not.toThrow();
});
test('should not fail when no handlers are provided', () => {
const { result } = renderHook(() => useCombinedHandler());
expect(() => result.current()).not.toThrow();
});
test('should allow the combined handler to be called multiple times', () => {
const handler1 = jest.fn();
const handler2 = jest.fn();
const { result } = renderHook(() => useCombinedHandler(handler1, handler2));
result.current('firstCall');
result.current('secondCall');
expect(handler1).toHaveBeenNthCalledWith(1, 'firstCall');
expect(handler1).toHaveBeenNthCalledWith(2, 'secondCall');
expect(handler2).toHaveBeenNthCalledWith(1, 'firstCall');
expect(handler2).toHaveBeenNthCalledWith(2, 'secondCall');
});
test('should work with asynchronous handlers', async () => {
const handler1 = jest.fn(async arg => await Promise.resolve(arg));
const handler2 = jest.fn(async arg => await Promise.resolve(arg));
const { result } = renderHook(() => useCombinedHandler(handler1, handler2));
result.current('asyncArg');
expect(handler1).toHaveBeenCalledWith('asyncArg');
expect(handler2).toHaveBeenCalledWith('asyncArg');
});
});
@@ -0,0 +1,69 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 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 { describe, expect, it, jest } from '@jest/globals';
import { act, renderHook } from '@testing-library/react';
import React from 'react';
import { useCombinedRef } from './useCombinedRef.js';
describe('useCombinedRef', () => {
it('should handle ref as function', () => {
const callbackRef = jest.fn();
const { result } = renderHook(() => useCombinedRef(callbackRef));
const testInstance = { test: true };
act(() => {
result.current(testInstance);
});
expect(callbackRef).toHaveBeenCalledWith(testInstance);
});
it('should handle ref as RefObject', () => {
const mutableRef = React.createRef();
const { result } = renderHook(() => useCombinedRef(mutableRef));
const testInstance = { test: true };
act(() => {
result.current(testInstance);
});
expect(mutableRef.current).toBe(testInstance);
});
it('should handle ref as null', () => {
const { result } = renderHook(() => useCombinedRef(null));
const testInstance = { test: true };
expect(() => {
act(() => {
result.current(testInstance);
});
}).not.toThrow();
});
it('should handle multiple refs', () => {
const callbackRef = jest.fn();
const mutableRef = React.createRef();
const anotherMutableRef = React.createRef();
const { result } = renderHook(() => useCombinedRef(callbackRef, mutableRef, anotherMutableRef));
const testInstance = { test: true };
act(() => {
result.current(testInstance);
});
expect(callbackRef).toHaveBeenCalledWith(testInstance);
expect(mutableRef.current).toBe(testInstance);
expect(anotherMutableRef.current).toBe(testInstance);
});
});
-17
View File
@@ -1,17 +0,0 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 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 { useCallback, useRef } from 'react';
const noop: any[] = [];
export function useFn<TArgs extends any[], TValue>(fn: (...args: TArgs) => TValue): (...args: TArgs) => TValue {
const ref = useRef(fn);
ref.current = fn;
return useCallback((...args: TArgs) => ref.current(...args), noop);
}
+1 -1
View File
@@ -31,6 +31,7 @@
"@cloudbeaver/core-dialogs": "^0",
"@cloudbeaver/core-events": "^0",
"@cloudbeaver/core-executor": "^0",
"@cloudbeaver/core-links": "^0",
"@cloudbeaver/core-localization": "^0",
"@cloudbeaver/core-navigation-tree": "^0",
"@cloudbeaver/core-product": "^0",
@@ -52,7 +53,6 @@
"@cloudbeaver/core-version": "^0",
"@cloudbeaver/core-version-update": "^0",
"@cloudbeaver/core-view": "^0",
"@cloudbeaver/core-website": "^0",
"mobx": "^6",
"react": "^18",
"react-dom": "^18"
@@ -17,6 +17,7 @@ import { coreConnectionsManifest } from '@cloudbeaver/core-connections';
import { coreDIManifest, type PluginManifest } from '@cloudbeaver/core-di';
import { coreDialogsManifest } from '@cloudbeaver/core-dialogs';
import { coreEventsManifest } from '@cloudbeaver/core-events';
import { coreLinksManifest } from '@cloudbeaver/core-links';
import { coreLocalizationManifest } from '@cloudbeaver/core-localization';
import { coreNavigationTree } from '@cloudbeaver/core-navigation-tree';
import { coreProductManifest } from '@cloudbeaver/core-product';
@@ -38,7 +39,6 @@ import { coreUIManifest } from '@cloudbeaver/core-ui';
import { coreVersionManifest } from '@cloudbeaver/core-version';
import { coreVersionUpdateManifest } from '@cloudbeaver/core-version-update';
import { coreViewManifest } from '@cloudbeaver/core-view';
import { coreWebsiteManifest } from '@cloudbeaver/core-website';
export const coreManifests: PluginManifest[] = [
{
@@ -64,7 +64,7 @@ export const coreManifests: PluginManifest[] = [
coreResourceManifest,
coreSDKManifest,
coreRootManifest,
coreWebsiteManifest,
coreLinksManifest,
coreBrowserSettingsManifest,
coreBrowserCookiesManifest,
coreProductManifest,
+3 -3
View File
@@ -45,6 +45,9 @@
{
"path": "../core-executor/tsconfig.json"
},
{
"path": "../core-links/tsconfig.json"
},
{
"path": "../core-localization/tsconfig.json"
},
@@ -107,9 +110,6 @@
},
{
"path": "../core-view/tsconfig.json"
},
{
"path": "../core-website/tsconfig.json"
}
],
"include": [
@@ -13,7 +13,14 @@ module.exports = {
testEnvironment: require.resolve('../tests/test.environment.js'),
rootDir: path.resolve('.'),
moduleFileExtensions: ['js', 'jsx', 'json'],
testMatch: ['<rootDir>/packages/*/dist/**/?(*.)+(spec|test).js?(x)', '<rootDir>/dist/**/?(*.)+(spec|test).js?(x)'],
testMatch: [
// unix
'<rootDir>/packages/*/dist/**/?(*.)+(spec|test).js?(x)',
'<rootDir>/dist/**/?(*.)+(spec|test).js?(x)',
// windows
'<rootDir>\\packages\\*\\dist\\**?(*.)+(spec|test).js?(x)',
'<rootDir>\\dist\\**?(*.)+(spec|test).js?(x)',
],
transformIgnorePatterns: [
'\\.pnp\\.[^\\/]+$',
'node_modules/(?!@ngrx|(?!deck.gl)|ng-dynamic)',
@@ -15,7 +15,8 @@
"clean": "rimraf --glob dist",
"lint": "eslint ./src/ --ext .ts,.tsx",
"validate-dependencies": "core-cli-validate-dependencies",
"update-ts-references": "yarn run clean && typescript-resolve-references"
"update-ts-references": "yarn run clean && typescript-resolve-references",
"test": "core-cli-test"
},
"dependencies": {
"@cloudbeaver/core-di": "^0",
@@ -24,6 +25,8 @@
},
"peerDependencies": {},
"devDependencies": {
"@jest/globals": "^29",
"@types/jest": "^29",
"typescript": "^5"
}
}
@@ -0,0 +1,78 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 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 { afterEach, beforeEach, describe, expect, it, jest } from '@jest/globals';
import { ClientActivityService, INACTIVE_PERIOD_TIME } from './ClientActivityService.js';
jest.useFakeTimers();
describe('ClientActivityService', () => {
let clientActivityService: ClientActivityService;
beforeEach(() => {
clientActivityService = new ClientActivityService();
jest.spyOn(global, 'setTimeout');
jest.spyOn(global, 'clearTimeout');
});
afterEach(() => {
jest.clearAllTimers();
jest.restoreAllMocks();
});
it('should initialize with isActive set to false', () => {
expect(clientActivityService.isActive).toBe(false);
});
it('should set isActive to true when updateActivity is called', () => {
clientActivityService.updateActivity();
expect(clientActivityService.isActive).toBe(true);
});
it('should reset activity after the timeout period', () => {
clientActivityService.updateActivity();
expect(clientActivityService.isActive).toBe(true);
jest.advanceTimersByTime(INACTIVE_PERIOD_TIME);
expect(clientActivityService.isActive).toBe(false);
});
it('should clear previous timer if updateActivity is called multiple times', () => {
clientActivityService.updateActivity();
expect(setTimeout).toHaveBeenCalledTimes(1);
jest.advanceTimersByTime(Math.random() * INACTIVE_PERIOD_TIME - 1);
clientActivityService.updateActivity();
expect(clearTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenCalledTimes(2);
});
it('should clear timer and reset activity when resetActivity is called', () => {
clientActivityService.updateActivity();
jest.advanceTimersByTime(Math.random() * INACTIVE_PERIOD_TIME - 1);
clientActivityService.resetActivity();
expect(clientActivityService.isActive).toBe(false);
expect(clearTimeout).toHaveBeenCalled();
});
it('should call onActiveStateChange executor with correct value', () => {
const onActiveStateChangeSpy = jest.spyOn(clientActivityService.onActiveStateChange, 'execute');
clientActivityService.updateActivity();
expect(onActiveStateChangeSpy).toHaveBeenCalledWith(true);
jest.advanceTimersByTime(INACTIVE_PERIOD_TIME);
expect(onActiveStateChangeSpy).toHaveBeenCalledWith(false);
});
});
@@ -10,7 +10,7 @@ import { makeObservable, observable } from 'mobx';
import { injectable } from '@cloudbeaver/core-di';
import { Executor, type IExecutor } from '@cloudbeaver/core-executor';
const INACTIVE_PERIOD_TIME = 1000 * 60;
export const INACTIVE_PERIOD_TIME = 1000 * 60;
@injectable()
export class ClientActivityService {
@@ -1,5 +1,5 @@
{
"name": "@cloudbeaver/core-website",
"name": "@cloudbeaver/core-links",
"type": "module",
"sideEffects": [
"src/**/*.css",
@@ -0,0 +1,20 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
export const GITHUB_LINKS = {
CLOUDBEAVER_REPO: 'https://github.com/dbeaver/cloudbeaver',
EE_DEPLOY_UPDATE: 'https://github.com/dbeaver/cloudbeaver-deploy?tab=readme-ov-file#updating-the-cluster',
TE_DEPLOY_UPDATE: 'https://github.com/dbeaver/team-edition-deploy?tab=readme-ov-file#server-version-update',
getDeployUpdateLink(distributed: boolean) {
if (distributed) {
return GITHUB_LINKS.TE_DEPLOY_UPDATE;
}
return GITHUB_LINKS.EE_DEPLOY_UPDATE;
},
};
@@ -0,0 +1,32 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
export const WEBSITE_LINKS = {
ROOT_PAGE: 'https://dbeaver.com/',
DATA_EDITOR_DOCUMENTATION_PAGE: 'https://dbeaver.com/docs/cloudbeaver/Data-editor/',
SQL_EDITOR_DOCUMENTATION_PAGE: 'https://dbeaver.com/docs/cloudbeaver/SQL-Editor/',
SERVER_CONFIGURATION_RESOURCE_QUOTAS_PAGE: 'https://dbeaver.com/docs/cloudbeaver/Server-configuration/#resource-quotas',
DATABASE_NAVIGATOR_DOCUMENTATION_PAGE: 'https://dbeaver.com/docs/cloudbeaver/Database-Navigator/',
ENTERPRISE_BUY_PRODUCT_PAGE: 'https://dbeaver.com/products/cloudbeaver-enterprise/',
TEAM_EDITION_BUY_PRODUCT_PAGE: 'https://dbeaver.com/products/team-edition/',
LATEST_COMMUNITY_VERSION_PAGE: 'https://dbeaver.com/product/cloudbeaver-ce-version.json',
TEAM_ARCHIVE: 'https://dbeaver.com/downloads-team',
CONTACT_PAGE: 'https://dbeaver.com/contact/',
getTeamArchiveById(id: string) {
return `${WEBSITE_LINKS.TEAM_ARCHIVE}/${id}`;
},
getProductBuyPage(distributed: boolean) {
if (distributed) {
return WEBSITE_LINKS.TEAM_EDITION_BUY_PRODUCT_PAGE;
}
return WEBSITE_LINKS.ENTERPRISE_BUY_PRODUCT_PAGE;
},
};
@@ -6,4 +6,5 @@
* you may not use this file except in compliance with the License.
*/
export * from './WebsiteLinks.js';
export * from './GithubLinks.js';
export * from './manifest.js';
@@ -7,9 +7,9 @@
*/
import type { PluginManifest } from '@cloudbeaver/core-di';
export const coreWebsiteManifest: PluginManifest = {
export const coreLinksManifest: PluginManifest = {
info: {
name: 'Core Website',
name: 'Core Links',
},
providers: [],

Some files were not shown because too many files have changed in this diff Show More