mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
dbeaver/pro#8486 feat: add DataReadonlyReason API for data sources (#4354)
* dbeaver/pro#8486 feat: add DataReadonlyReason API for data sources Previously TableStatusIndicator relied on model.isReadonly() which lost reasons. This caused the "Read-only connection" tooltip to show in grouping panel even on editable connections — the connection wasn't read-only, the presentation was. Each data source now declares its specific reason via getReadonlyReason(). UI maps the typed reason to an accurate message * dbeaver/pro#8486 refactor: remove DataReadonlyReason references and use hasFeature flag --------- Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
"dependencies": {
|
||||
"@cloudbeaver/core-blocks": "workspace:*",
|
||||
"@cloudbeaver/core-browser": "workspace:*",
|
||||
"@cloudbeaver/core-connections": "workspace:*",
|
||||
"@cloudbeaver/core-data-context": "workspace:*",
|
||||
"@cloudbeaver/core-di": "workspace:*",
|
||||
"@cloudbeaver/core-dialogs": "workspace:*",
|
||||
|
||||
+31
-7
@@ -9,8 +9,9 @@ import { SqlRowIdentifierState, type SqlResultColumn } from '@cloudbeaver/core-s
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { IconOrImage, s, useS, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
import { isResultSetDataSource } from '@cloudbeaver/plugin-data-viewer';
|
||||
import { IconOrImage, s, useResource, useS, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
import { ConnectionInfoResource, createConnectionParam } from '@cloudbeaver/core-connections';
|
||||
import { DatabaseDataFeature, isResultSetDataSource } from '@cloudbeaver/plugin-data-viewer';
|
||||
|
||||
import { DataGridContext } from '../DataGridContext.js';
|
||||
import { TableDataContext } from '../TableDataContext.js';
|
||||
@@ -20,16 +21,27 @@ import styles from './TableStatusIndicator.module.css';
|
||||
export const TableStatusIndicator = observer(function TableStatusIndicator() {
|
||||
const dataGridContext = useContext(DataGridContext);
|
||||
const tableDataContext = useContext(TableDataContext);
|
||||
const readOnlyConnection = dataGridContext.model.isReadonly(dataGridContext.resultIndex);
|
||||
const translate = useTranslate();
|
||||
|
||||
const source = dataGridContext.model.source;
|
||||
const resultSetSource = isResultSetDataSource(source) ? source : null;
|
||||
|
||||
/* We do NOT use `model.isReadonly()` here —
|
||||
that method aggregates several unrelated reasons
|
||||
and loses information about WHY editing isn't allowed. */
|
||||
const contextInfo = resultSetSource?.executionContext?.context;
|
||||
const connectionKey = contextInfo ? createConnectionParam(contextInfo.projectId, contextInfo.connectionId) : null;
|
||||
const connectionInfoLoader = useResource(TableStatusIndicator, ConnectionInfoResource, connectionKey);
|
||||
const readOnlyConnection = connectionInfoLoader.data?.readOnly ?? false;
|
||||
|
||||
const readOnlyPresentation = source.hasFeature(DatabaseDataFeature.Grouping);
|
||||
|
||||
const style = useS(styles);
|
||||
|
||||
if (!tableDataContext || !dataGridContext) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const source = dataGridContext.model.source;
|
||||
const resultSetSource = isResultSetDataSource(source) ? source : null;
|
||||
const rowIdentifierInfo = resultSetSource?.getRowIdentifierInfo(dataGridContext.resultIndex);
|
||||
const hasRowIdentifier = resultSetSource?.hasElementIdentifier(dataGridContext.resultIndex);
|
||||
|
||||
@@ -43,7 +55,10 @@ export const TableStatusIndicator = observer(function TableStatusIndicator() {
|
||||
const isPrimaryKey = rowIdentifierInfo?.state === SqlRowIdentifierState.PrimaryKey;
|
||||
const tooltipParts: string[] = [];
|
||||
|
||||
if (readOnlyConnection) {
|
||||
// Presentation-level read-only takes precedence over connection-level read-only.
|
||||
if (readOnlyPresentation) {
|
||||
tooltipParts.push(translate('data_grid_table_readonly_presentation_tooltip'));
|
||||
} else if (readOnlyConnection) {
|
||||
tooltipParts.push(translate('data_grid_table_readonly_connection_tooltip'));
|
||||
}
|
||||
|
||||
@@ -59,6 +74,15 @@ export const TableStatusIndicator = observer(function TableStatusIndicator() {
|
||||
tooltipParts.push(translate('data_grid_table_virtual_key_tooltip'));
|
||||
}
|
||||
|
||||
const showLockIcon = readOnlyConnection || readOnlyPresentation;
|
||||
|
||||
// Hide the entire indicator when there's nothing meaningful to display (Session Manager)
|
||||
const hasInfo = showLockIcon || !!readOnlyStatus || hasRowIdentifier || isVirtualKey || isPrimaryKey;
|
||||
|
||||
if (!hasInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const tooltip = tooltipParts.join('\n');
|
||||
|
||||
return (
|
||||
@@ -66,7 +90,7 @@ export const TableStatusIndicator = observer(function TableStatusIndicator() {
|
||||
title={tooltip}
|
||||
className="tw:absolute tw:top-1/2 tw:left-1 tw:-translate-y-1/2 tw:z-1 tw:pointer-events-auto tw:flex tw:items-center tw:gap-1 tw:cursor-help"
|
||||
>
|
||||
{readOnlyConnection && <IconOrImage icon="/icons/lock.png" className="tw:w-2.5 tw:cursor-help" />}
|
||||
{showLockIcon && <IconOrImage icon="/icons/lock.png" className="tw:w-2.5 tw:cursor-help" />}
|
||||
<div
|
||||
className={s(
|
||||
style,
|
||||
|
||||
@@ -25,6 +25,7 @@ export default [
|
||||
['data_grid_table_context_menu_filter_dialog_title', 'Wert bearbeiten'],
|
||||
['data_grid_table_index_column_tooltip', 'Wählen ganze Tabelle aus'],
|
||||
['data_grid_table_readonly_connection_tooltip', 'Schreibgeschützte Verbindung'],
|
||||
['data_grid_table_readonly_presentation_tooltip', 'Die aktive Darstellung unterstützt keine Datenbearbeitung'],
|
||||
['data_grid_table_no_key_found_tooltip', 'Kein eindeutiger Schlüssel gefunden. Datenänderung nicht möglich.'],
|
||||
['data_grid_table_virtual_key_tooltip', 'Virtueller Schlüssel'],
|
||||
['data_grid_table_key_column_tooltip', 'Schlüsselspalte'],
|
||||
|
||||
@@ -31,6 +31,7 @@ export default [
|
||||
['data_grid_table_context_menu_save_value_error', 'Failed to save value'],
|
||||
['data_grid_table_index_column_tooltip', 'Select whole table'],
|
||||
['data_grid_table_readonly_connection_tooltip', 'Read-only connection'],
|
||||
['data_grid_table_readonly_presentation_tooltip', 'Read-only: Active presentation does not support data editing'],
|
||||
['data_grid_table_no_key_found_tooltip', 'No unique key was found. Data modification is not possible.'],
|
||||
['data_grid_table_virtual_key_tooltip', 'Virtual key'],
|
||||
['data_grid_table_key_column_tooltip', 'Key column'],
|
||||
|
||||
@@ -34,6 +34,7 @@ export default [
|
||||
['data_grid_table_virtual_key_tooltip', 'Clé virtuelle'],
|
||||
['data_grid_table_key_column_tooltip', 'Colonne clé'],
|
||||
['data_grid_table_readonly_connection_tooltip', 'Connexion en lecture seule'],
|
||||
['data_grid_table_readonly_presentation_tooltip', 'La présentation active ne prend pas en charge la modification des données'],
|
||||
['plugin_data_spreadsheet_new_settings_disable', 'Désactiver la présentation de la table'],
|
||||
['plugin_data_spreadsheet_new_settings_description_label', 'Show columns description'],
|
||||
['plugin_data_spreadsheet_new_settings_description_label_description', 'Description will be shown under the column names in the table header'],
|
||||
|
||||
@@ -30,6 +30,7 @@ export default [
|
||||
['data_grid_table_context_menu_save_value_error', 'Failed to save value'],
|
||||
['data_grid_table_index_column_tooltip', 'Seleziona tutta la tabella'],
|
||||
['data_grid_table_readonly_connection_tooltip', 'Connessione in sola lettura'],
|
||||
['data_grid_table_readonly_presentation_tooltip', 'La presentazione attiva non supporta la modifica dei dati'],
|
||||
['data_grid_table_no_key_found_tooltip', 'Nessuna chiave univoca è stata trovata. La modifica dei dati non è possibile.'],
|
||||
['data_grid_table_virtual_key_tooltip', 'Chiave virtuale'],
|
||||
['data_grid_table_key_column_tooltip', 'Colonna chiave'],
|
||||
|
||||
@@ -31,6 +31,7 @@ export default [
|
||||
['data_grid_table_context_menu_save_value_error', 'Не удалось сохранить значение'],
|
||||
['data_grid_table_index_column_tooltip', 'Выбрать всю таблицу'],
|
||||
['data_grid_table_readonly_connection_tooltip', 'Подключение только для чтения'],
|
||||
['data_grid_table_readonly_presentation_tooltip', 'Активное представление не поддерживает редактирование данных'],
|
||||
['data_grid_table_no_key_found_tooltip', 'Не найден уникальный ключ. Изменение данных невозможно.'],
|
||||
['data_grid_table_virtual_key_tooltip', 'Виртуальный ключ'],
|
||||
['data_grid_table_key_column_tooltip', 'Ключевая колонка'],
|
||||
|
||||
@@ -31,6 +31,7 @@ export default [
|
||||
['data_grid_table_context_menu_save_value_error', 'Không thể lưu giá trị'],
|
||||
['data_grid_table_index_column_tooltip', 'Chọn toàn bộ bảng'],
|
||||
['data_grid_table_readonly_connection_tooltip', 'Kết nối chỉ đọc'],
|
||||
['data_grid_table_readonly_presentation_tooltip', 'Trình bày đang dùng không hỗ trợ chỉnh sửa dữ liệu'],
|
||||
['data_grid_table_no_key_found_tooltip', 'Không tìm thấy khóa duy nhất. Không thể sửa đổi dữ liệu.'],
|
||||
['data_grid_table_virtual_key_tooltip', 'Khóa ảo'],
|
||||
['data_grid_table_key_column_tooltip', 'Cột khóa'],
|
||||
|
||||
@@ -34,6 +34,7 @@ export default [
|
||||
['data_grid_table_virtual_key_tooltip', '虚拟键'],
|
||||
['data_grid_table_key_column_tooltip', '键列'],
|
||||
['data_grid_table_readonly_connection_tooltip', '只读连接'],
|
||||
['data_grid_table_readonly_presentation_tooltip', '当前展示形式不支持数据编辑'],
|
||||
['plugin_data_spreadsheet_new_settings_disable', '禁用表显示'],
|
||||
['plugin_data_spreadsheet_new_settings_description_label', '显示列描述'],
|
||||
['plugin_data_spreadsheet_new_settings_description_label_description', '描述将显示在表头的列名下方'],
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
{
|
||||
"path": "../core-cli"
|
||||
},
|
||||
{
|
||||
"path": "../core-connections"
|
||||
},
|
||||
{
|
||||
"path": "../core-data-context"
|
||||
},
|
||||
|
||||
@@ -43,9 +43,10 @@ export interface IRowIdentifierInfo {
|
||||
identifier: SqlRowIdentifier | null;
|
||||
}
|
||||
|
||||
export abstract class ResultSetDataSource<TOptions extends IDatabaseDataOptions = IDatabaseDataOptions>
|
||||
extends DatabaseDataSource<TOptions, IDatabaseResultSet>
|
||||
{
|
||||
export abstract class ResultSetDataSource<TOptions extends IDatabaseDataOptions = IDatabaseDataOptions> extends DatabaseDataSource<
|
||||
TOptions,
|
||||
IDatabaseResultSet
|
||||
> {
|
||||
executionContext: IConnectionExecutionContext | null;
|
||||
totalCountRequestTask: ITask<number> | null;
|
||||
private keepExecutionContextOnDispose: boolean;
|
||||
|
||||
@@ -2957,6 +2957,7 @@ __metadata:
|
||||
"@cloudbeaver/core-blocks": "workspace:*"
|
||||
"@cloudbeaver/core-browser": "workspace:*"
|
||||
"@cloudbeaver/core-cli": "workspace:*"
|
||||
"@cloudbeaver/core-connections": "workspace:*"
|
||||
"@cloudbeaver/core-data-context": "workspace:*"
|
||||
"@cloudbeaver/core-di": "workspace:*"
|
||||
"@cloudbeaver/core-dialogs": "workspace:*"
|
||||
|
||||
Reference in New Issue
Block a user