mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-16 14:50:15 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f120d6a4f | ||
|
|
d3be30f55b | ||
|
|
2c7df05c45 | ||
|
|
11167d5802 | ||
|
|
4cfb27b33b | ||
|
|
0126b204c0 | ||
|
|
6dbcfe408e | ||
|
|
de2d9f029f | ||
|
|
2f895f6dab | ||
|
|
c7af629d8a | ||
|
|
1782ce45a7 | ||
|
|
51e0218682 | ||
|
|
e08353f13f | ||
|
|
383952c60e | ||
|
|
5282175b7c | ||
|
|
b4db34ea48 | ||
|
|
9672e5b3b4 | ||
|
|
48a7a4f806 | ||
|
|
cbceee6ec3 | ||
|
|
ffe0a18828 | ||
|
|
115cfb3bde | ||
|
|
e62538b583 | ||
|
|
5255e9949d | ||
|
|
8e4faa5935 | ||
|
|
39ad2994b2 | ||
|
|
00788615e1 | ||
|
|
ac937698bd | ||
|
|
be88091950 | ||
|
|
9dcaec0c09 | ||
|
|
f677f48890 | ||
|
|
a44cadbb8a | ||
|
|
5c992dc8ee | ||
|
|
05f85db7e6 |
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
Vendored
+1
-1
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
@@ -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: .
|
||||
|
||||
@@ -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>
|
||||
|
||||
+4
@@ -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)
|
||||
|
||||
+5
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
+8
-3
@@ -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);
|
||||
}
|
||||
|
||||
+2
@@ -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));
|
||||
}
|
||||
|
||||
+1
-1
@@ -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();
|
||||
|
||||
|
||||
+8
-2
@@ -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
|
||||
|
||||
+21
-17
@@ -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");
|
||||
}
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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");
|
||||
}
|
||||
|
||||
+4
-6
@@ -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) {
|
||||
|
||||
+3
-1
@@ -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 {
|
||||
|
||||
+8
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -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>
|
||||
|
||||
+2
-2
@@ -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>
|
||||
|
||||
+4
-1
@@ -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>
|
||||
|
||||
+9
-4
@@ -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: .
|
||||
|
||||
@@ -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
@@ -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">
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+2
-3
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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';
|
||||
+2
-2
@@ -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
Reference in New Issue
Block a user