ydb-embedded-ui 4.12.0 → 4.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/components/InfoViewer/formatters/common.ts +4 -2
- package/dist/components/InfoViewer/i18n/en.json +4 -0
- package/dist/components/InfoViewer/i18n/index.ts +11 -0
- package/dist/components/InfoViewer/i18n/ru.json +4 -0
- package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +5 -2
- package/dist/containers/Tenant/Info/ExternalDataSource/ExternalDataSource.scss +5 -0
- package/dist/containers/Tenant/Info/ExternalDataSource/ExternalDataSource.tsx +81 -0
- package/dist/containers/Tenant/Info/ExternalTable/ExternalTable.scss +5 -0
- package/dist/containers/Tenant/Info/ExternalTable/ExternalTable.tsx +103 -0
- package/dist/containers/Tenant/Info/i18n/en.json +8 -0
- package/dist/containers/Tenant/Info/i18n/index.ts +11 -0
- package/dist/containers/Tenant/Info/i18n/ru.json +8 -0
- package/dist/containers/Tenant/ObjectSummary/ObjectSummary.scss +4 -4
- package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +10 -3
- package/dist/containers/Tenant/Query/QueryEditor/QueryEditor.js +4 -6
- package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +8 -2
- package/dist/containers/Tenant/i18n/en.json +12 -2
- package/dist/containers/Tenant/i18n/ru.json +11 -1
- package/dist/containers/Tenant/utils/schemaActions.ts +75 -20
- package/dist/index.js +7 -3
- package/dist/styles/constants.scss +3 -0
- package/dist/utils/hooks/i18n/en.json +3 -0
- package/dist/utils/hooks/i18n/index.ts +11 -0
- package/dist/utils/hooks/i18n/ru.json +3 -0
- package/dist/utils/hooks/index.ts +1 -0
- package/dist/utils/hooks/useQueryModes.ts +34 -0
- package/dist/utils/query.ts +5 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [4.13.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.12.0...v4.13.0) (2023-08-04)
|
4
|
+
|
5
|
+
|
6
|
+
### Features
|
7
|
+
|
8
|
+
* info and summary tabs for external objects ([#493](https://github.com/ydb-platform/ydb-embedded-ui/issues/493)) ([88d9041](https://github.com/ydb-platform/ydb-embedded-ui/commit/88d9041f080f13046aeaf55765609dbc13b87285))
|
9
|
+
|
10
|
+
|
11
|
+
### Bug Fixes
|
12
|
+
|
13
|
+
* **SchemaTree:** add actions to external objects ([#497](https://github.com/ydb-platform/ydb-embedded-ui/issues/497)) ([5029579](https://github.com/ydb-platform/ydb-embedded-ui/commit/5029579796dd5fb985005f39e9ef8daf142366d0))
|
14
|
+
* **SchemaTree:** set required query mode for tree actions ([#491](https://github.com/ydb-platform/ydb-embedded-ui/issues/491)) ([ccd1eda](https://github.com/ydb-platform/ydb-embedded-ui/commit/ccd1edac0d84357cd605c9d131c99890449d8bd8))
|
15
|
+
|
3
16
|
## [4.12.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.11.1...v4.12.0) (2023-08-02)
|
4
17
|
|
5
18
|
|
@@ -3,13 +3,15 @@ import {formatDateTime} from '../../../utils';
|
|
3
3
|
|
4
4
|
import {createInfoFormatter} from '../utils';
|
5
5
|
|
6
|
+
import i18n from '../i18n';
|
7
|
+
|
6
8
|
export const formatCommonItem = createInfoFormatter<TDirEntry>({
|
7
9
|
values: {
|
8
10
|
PathType: (value) => value?.substring('EPathType'.length),
|
9
11
|
CreateStep: formatDateTime,
|
10
12
|
},
|
11
13
|
labels: {
|
12
|
-
PathType: '
|
13
|
-
CreateStep: '
|
14
|
+
PathType: i18n('common.type'),
|
15
|
+
CreateStep: i18n('common.created'),
|
14
16
|
},
|
15
17
|
});
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import {i18n, Lang} from '../../../utils/i18n';
|
2
|
+
|
3
|
+
import en from './en.json';
|
4
|
+
import ru from './ru.json';
|
5
|
+
|
6
|
+
const COMPONENT = 'ydb-components-info-viewer';
|
7
|
+
|
8
|
+
i18n.registerKeyset(Lang.En, COMPONENT, en);
|
9
|
+
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
|
10
|
+
|
11
|
+
export default i18n.keyset(COMPONENT);
|
@@ -27,6 +27,9 @@ import {
|
|
27
27
|
isPathTypeWithTopic,
|
28
28
|
} from '../../utils/schema';
|
29
29
|
|
30
|
+
import {ExternalTableInfo} from '../../Info/ExternalTable/ExternalTable';
|
31
|
+
import {ExternalDataSourceInfo} from '../../Info/ExternalDataSource/ExternalDataSource';
|
32
|
+
|
30
33
|
import {TopicInfo} from './TopicInfo';
|
31
34
|
import {ChangefeedInfo} from './ChangefeedInfo';
|
32
35
|
import {TableInfo} from './TableInfo';
|
@@ -124,8 +127,8 @@ function Overview({type, tenantName}: OverviewProps) {
|
|
124
127
|
<ChangefeedInfo data={data} topic={additionalData?.[0]} />
|
125
128
|
),
|
126
129
|
[EPathType.EPathTypePersQueueGroup]: () => <TopicInfo data={data} />,
|
127
|
-
[EPathType.EPathTypeExternalTable]:
|
128
|
-
[EPathType.EPathTypeExternalDataSource]:
|
130
|
+
[EPathType.EPathTypeExternalTable]: () => <ExternalTableInfo data={data} />,
|
131
|
+
[EPathType.EPathTypeExternalDataSource]: () => <ExternalDataSourceInfo data={data} />,
|
129
132
|
};
|
130
133
|
|
131
134
|
return (
|
@@ -0,0 +1,81 @@
|
|
1
|
+
import block from 'bem-cn-lite';
|
2
|
+
|
3
|
+
import type {TEvDescribeSchemeResult} from '../../../../types/api/schema';
|
4
|
+
import {useTypedSelector} from '../../../../utils/hooks';
|
5
|
+
|
6
|
+
import {InfoViewer, InfoViewerItem} from '../../../../components/InfoViewer';
|
7
|
+
import {formatCommonItem} from '../../../../components/InfoViewer/formatters';
|
8
|
+
import EntityStatus from '../../../../components/EntityStatus/EntityStatus';
|
9
|
+
import {ResponseError} from '../../../../components/Errors/ResponseError';
|
10
|
+
|
11
|
+
import {getEntityName} from '../../utils';
|
12
|
+
|
13
|
+
import i18n from '../i18n';
|
14
|
+
import './ExternalDataSource.scss';
|
15
|
+
|
16
|
+
const b = block('ydb-external-data-source-info');
|
17
|
+
|
18
|
+
const prepareExternalDataSourceSummary = (data: TEvDescribeSchemeResult): InfoViewerItem[] => {
|
19
|
+
return [
|
20
|
+
{
|
21
|
+
label: i18n('external-objects.source-type'),
|
22
|
+
value: data.PathDescription?.ExternalDataSourceDescription?.SourceType,
|
23
|
+
},
|
24
|
+
formatCommonItem('CreateStep', data.PathDescription?.Self?.CreateStep),
|
25
|
+
];
|
26
|
+
};
|
27
|
+
|
28
|
+
const prepareExternalDataSourceInfo = (data: TEvDescribeSchemeResult): InfoViewerItem[] => {
|
29
|
+
const {Location, Auth} = data.PathDescription?.ExternalDataSourceDescription || {};
|
30
|
+
|
31
|
+
return [
|
32
|
+
...prepareExternalDataSourceSummary(data),
|
33
|
+
{
|
34
|
+
label: i18n('external-objects.location'),
|
35
|
+
value: (
|
36
|
+
<EntityStatus
|
37
|
+
name={Location}
|
38
|
+
showStatus={false}
|
39
|
+
hasClipboardButton
|
40
|
+
clipboardButtonAlwaysVisible
|
41
|
+
className={b('location')}
|
42
|
+
/>
|
43
|
+
),
|
44
|
+
},
|
45
|
+
{
|
46
|
+
label: i18n('external-objects.auth-method'),
|
47
|
+
value: Auth?.ServiceAccount
|
48
|
+
? i18n('external-objects.auth-method.service-account')
|
49
|
+
: i18n('external-objects.auth-method.none'),
|
50
|
+
},
|
51
|
+
];
|
52
|
+
};
|
53
|
+
|
54
|
+
interface ExternalDataSourceProps {
|
55
|
+
data?: TEvDescribeSchemeResult;
|
56
|
+
prepareData: (data: TEvDescribeSchemeResult) => InfoViewerItem[];
|
57
|
+
}
|
58
|
+
|
59
|
+
const ExternalDataSource = ({data, prepareData}: ExternalDataSourceProps) => {
|
60
|
+
const entityName = getEntityName(data?.PathDescription);
|
61
|
+
|
62
|
+
const {error: schemaError} = useTypedSelector((state) => state.schema);
|
63
|
+
|
64
|
+
if (schemaError) {
|
65
|
+
return <ResponseError error={schemaError} />;
|
66
|
+
}
|
67
|
+
|
68
|
+
if (!data) {
|
69
|
+
return <div className="error">No {entityName} data</div>;
|
70
|
+
}
|
71
|
+
|
72
|
+
return <InfoViewer title={entityName} info={prepareData(data)} />;
|
73
|
+
};
|
74
|
+
|
75
|
+
export const ExternalDataSourceInfo = ({data}: {data?: TEvDescribeSchemeResult}) => {
|
76
|
+
return <ExternalDataSource data={data} prepareData={prepareExternalDataSourceInfo} />;
|
77
|
+
};
|
78
|
+
|
79
|
+
export const ExternalDataSourceSummary = ({data}: {data?: TEvDescribeSchemeResult}) => {
|
80
|
+
return <ExternalDataSource data={data} prepareData={prepareExternalDataSourceSummary} />;
|
81
|
+
};
|
@@ -0,0 +1,103 @@
|
|
1
|
+
import {useLocation} from 'react-router';
|
2
|
+
import block from 'bem-cn-lite';
|
3
|
+
|
4
|
+
import type {TEvDescribeSchemeResult} from '../../../../types/api/schema';
|
5
|
+
import {useTypedSelector} from '../../../../utils/hooks';
|
6
|
+
import {createHref, parseQuery} from '../../../../routes';
|
7
|
+
import {formatCommonItem} from '../../../../components/InfoViewer/formatters';
|
8
|
+
import {InfoViewer, InfoViewerItem} from '../../../../components/InfoViewer';
|
9
|
+
import {ExternalLinkWithIcon} from '../../../../components/ExternalLinkWithIcon/ExternalLinkWithIcon';
|
10
|
+
import EntityStatus from '../../../../components/EntityStatus/EntityStatus';
|
11
|
+
import {ResponseError} from '../../../../components/Errors/ResponseError';
|
12
|
+
|
13
|
+
import {getEntityName} from '../../utils';
|
14
|
+
|
15
|
+
import i18n from '../i18n';
|
16
|
+
import './ExternalTable.scss';
|
17
|
+
|
18
|
+
const b = block('ydb-external-table-info');
|
19
|
+
|
20
|
+
const prepareExternalTableSummary = (
|
21
|
+
data: TEvDescribeSchemeResult,
|
22
|
+
pathToDataSource: string,
|
23
|
+
): InfoViewerItem[] => {
|
24
|
+
const {CreateStep} = data.PathDescription?.Self || {};
|
25
|
+
const {SourceType, DataSourcePath} = data.PathDescription?.ExternalTableDescription || {};
|
26
|
+
|
27
|
+
const dataSourceName = DataSourcePath?.split('/').pop();
|
28
|
+
|
29
|
+
return [
|
30
|
+
{label: i18n('external-objects.source-type'), value: SourceType},
|
31
|
+
formatCommonItem('CreateStep', CreateStep),
|
32
|
+
{
|
33
|
+
label: i18n('external-objects.data-source'),
|
34
|
+
value: DataSourcePath && (
|
35
|
+
<span title={DataSourcePath}>
|
36
|
+
<ExternalLinkWithIcon title={dataSourceName || ''} url={pathToDataSource} />
|
37
|
+
</span>
|
38
|
+
),
|
39
|
+
},
|
40
|
+
];
|
41
|
+
};
|
42
|
+
|
43
|
+
const prepareExternalTableInfo = (
|
44
|
+
data: TEvDescribeSchemeResult,
|
45
|
+
pathToDataSource: string,
|
46
|
+
): InfoViewerItem[] => {
|
47
|
+
const location = data.PathDescription?.ExternalTableDescription?.Location;
|
48
|
+
|
49
|
+
return [
|
50
|
+
...prepareExternalTableSummary(data, pathToDataSource),
|
51
|
+
{
|
52
|
+
label: i18n('external-objects.location'),
|
53
|
+
value: (
|
54
|
+
<EntityStatus
|
55
|
+
name={location}
|
56
|
+
showStatus={false}
|
57
|
+
hasClipboardButton
|
58
|
+
clipboardButtonAlwaysVisible
|
59
|
+
className={b('location')}
|
60
|
+
/>
|
61
|
+
),
|
62
|
+
},
|
63
|
+
];
|
64
|
+
};
|
65
|
+
|
66
|
+
interface ExternalTableProps {
|
67
|
+
data?: TEvDescribeSchemeResult;
|
68
|
+
prepareData: (data: TEvDescribeSchemeResult, pathToDataSource: string) => InfoViewerItem[];
|
69
|
+
}
|
70
|
+
|
71
|
+
const ExternalTable = ({data, prepareData}: ExternalTableProps) => {
|
72
|
+
const location = useLocation();
|
73
|
+
const query = parseQuery(location);
|
74
|
+
|
75
|
+
// embedded version could be located in some folder (e.g. host/some_folder/app_router_path)
|
76
|
+
// window.location has the full pathname, while location from router ignores path to project
|
77
|
+
const pathToDataSource = createHref(window.location.pathname, undefined, {
|
78
|
+
...query,
|
79
|
+
schema: data?.PathDescription?.ExternalTableDescription?.DataSourcePath,
|
80
|
+
});
|
81
|
+
|
82
|
+
const entityName = getEntityName(data?.PathDescription);
|
83
|
+
|
84
|
+
const {error: schemaError} = useTypedSelector((state) => state.schema);
|
85
|
+
|
86
|
+
if (schemaError) {
|
87
|
+
return <ResponseError error={schemaError} />;
|
88
|
+
}
|
89
|
+
|
90
|
+
if (!data) {
|
91
|
+
return <div className="error">No {entityName} data</div>;
|
92
|
+
}
|
93
|
+
|
94
|
+
return <InfoViewer title={entityName} info={prepareData(data, pathToDataSource)} />;
|
95
|
+
};
|
96
|
+
|
97
|
+
export const ExternalTableInfo = ({data}: {data?: TEvDescribeSchemeResult}) => {
|
98
|
+
return <ExternalTable data={data} prepareData={prepareExternalTableInfo} />;
|
99
|
+
};
|
100
|
+
|
101
|
+
export const ExternalTableSummary = ({data}: {data?: TEvDescribeSchemeResult}) => {
|
102
|
+
return <ExternalTable data={data} prepareData={prepareExternalTableSummary} />;
|
103
|
+
};
|
@@ -0,0 +1,8 @@
|
|
1
|
+
{
|
2
|
+
"external-objects.source-type": "Source Type",
|
3
|
+
"external-objects.data-source": "Data Source",
|
4
|
+
"external-objects.location": "Location",
|
5
|
+
"external-objects.auth-method": "Auth Method",
|
6
|
+
"external-objects.auth-method.none": "None",
|
7
|
+
"external-objects.auth-method.service-account": "Service Account"
|
8
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import {i18n, Lang} from '../../../../utils/i18n';
|
2
|
+
|
3
|
+
import en from './en.json';
|
4
|
+
import ru from './ru.json';
|
5
|
+
|
6
|
+
const COMPONENT = 'ydb-tenant-objects-info';
|
7
|
+
|
8
|
+
i18n.registerKeyset(Lang.En, COMPONENT, en);
|
9
|
+
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
|
10
|
+
|
11
|
+
export default i18n.keyset(COMPONENT);
|
@@ -0,0 +1,8 @@
|
|
1
|
+
{
|
2
|
+
"external-objects.source-type": "Тип источника",
|
3
|
+
"external-objects.data-source": "Источник",
|
4
|
+
"external-objects.location": "Расположение",
|
5
|
+
"external-objects.auth-method": "Авторизация",
|
6
|
+
"external-objects.auth-method.none": "Нет",
|
7
|
+
"external-objects.auth-method.service-account": "Сервисный аккаунт"
|
8
|
+
}
|
@@ -86,6 +86,10 @@
|
|
86
86
|
&__info-controls {
|
87
87
|
display: flex;
|
88
88
|
gap: 4px;
|
89
|
+
|
90
|
+
.yc-button__text {
|
91
|
+
margin: 0 6px;
|
92
|
+
}
|
89
93
|
}
|
90
94
|
|
91
95
|
&__info-action-button {
|
@@ -148,8 +152,4 @@
|
|
148
152
|
background-color: transparent;
|
149
153
|
}
|
150
154
|
}
|
151
|
-
|
152
|
-
.yc-button__text {
|
153
|
-
margin: 0 6px;
|
154
|
-
}
|
155
155
|
}
|
@@ -41,6 +41,8 @@ import {
|
|
41
41
|
import {SchemaTree} from '../Schema/SchemaTree/SchemaTree';
|
42
42
|
import {SchemaViewer} from '../Schema/SchemaViewer/SchemaViewer';
|
43
43
|
import {Acl} from '../Acl/Acl';
|
44
|
+
import {ExternalTableSummary} from '../Info/ExternalTable/ExternalTable';
|
45
|
+
import {ExternalDataSourceSummary} from '../Info/ExternalDataSource/ExternalDataSource';
|
44
46
|
|
45
47
|
import {TenantTabsGroups, TENANT_INFO_TABS, TENANT_SCHEMA_TAB} from '../TenantPages';
|
46
48
|
import {
|
@@ -50,9 +52,10 @@ import {
|
|
50
52
|
} from '../utils/paneVisibilityToggleHelpers';
|
51
53
|
import {isColumnEntityType, isExternalTable, isIndexTable, isTableType} from '../utils/schema';
|
52
54
|
|
53
|
-
import './ObjectSummary.scss';
|
54
55
|
import i18n from '../i18n';
|
55
56
|
|
57
|
+
import './ObjectSummary.scss';
|
58
|
+
|
56
59
|
const b = cn('object-summary');
|
57
60
|
|
58
61
|
const getInitialIsSummaryCollapsed = () => {
|
@@ -197,8 +200,12 @@ export function ObjectSummary({
|
|
197
200
|
[EPathType.EPathTypePersQueueGroup]: () => (
|
198
201
|
<PersQueueGroupOverview data={currentObjectData} />
|
199
202
|
),
|
200
|
-
[EPathType.EPathTypeExternalTable]:
|
201
|
-
|
203
|
+
[EPathType.EPathTypeExternalTable]: () => (
|
204
|
+
<ExternalTableSummary data={currentObjectData} />
|
205
|
+
),
|
206
|
+
[EPathType.EPathTypeExternalDataSource]: () => (
|
207
|
+
<ExternalDataSourceSummary data={currentObjectData} />
|
208
|
+
),
|
202
209
|
};
|
203
210
|
|
204
211
|
let component =
|
@@ -24,12 +24,11 @@ import {
|
|
24
24
|
DEFAULT_IS_QUERY_RESULT_COLLAPSED,
|
25
25
|
DEFAULT_SIZE_RESULT_PANE_KEY,
|
26
26
|
SAVED_QUERIES_KEY,
|
27
|
-
QUERY_INITIAL_MODE_KEY,
|
28
27
|
ENABLE_ADDITIONAL_QUERY_MODES,
|
29
28
|
LAST_USED_QUERY_ACTION_KEY,
|
30
29
|
} from '../../../../utils/constants';
|
31
|
-
import {useSetting} from '../../../../utils/hooks';
|
32
|
-
import {QUERY_ACTIONS, QUERY_MODES} from '../../../../utils/query';
|
30
|
+
import {useSetting, useQueryModes} from '../../../../utils/hooks';
|
31
|
+
import {QUERY_ACTIONS, QUERY_MODES, isNewQueryMode} from '../../../../utils/query';
|
33
32
|
|
34
33
|
import {
|
35
34
|
PaneVisibilityActionTypes,
|
@@ -91,13 +90,12 @@ function QueryEditor(props) {
|
|
91
90
|
const [resultType, setResultType] = useState(RESULT_TYPES.EXECUTE);
|
92
91
|
|
93
92
|
const [isResultLoaded, setIsResultLoaded] = useState(false);
|
94
|
-
const [queryMode, setQueryMode] =
|
93
|
+
const [queryMode, setQueryMode] = useQueryModes();
|
95
94
|
const [enableAdditionalQueryModes] = useSetting(ENABLE_ADDITIONAL_QUERY_MODES);
|
96
95
|
const [lastUsedQueryAction, setLastUsedQueryAction] = useSetting(LAST_USED_QUERY_ACTION_KEY);
|
97
96
|
|
98
97
|
useEffect(() => {
|
99
|
-
|
100
|
-
if (!enableAdditionalQueryModes && isNewQueryMode) {
|
98
|
+
if (isNewQueryMode(queryMode) && !enableAdditionalQueryModes) {
|
101
99
|
setQueryMode(QUERY_MODES.script);
|
102
100
|
}
|
103
101
|
}, [enableAdditionalQueryModes, queryMode, setQueryMode]);
|
@@ -3,8 +3,9 @@ import {useDispatch} from 'react-redux';
|
|
3
3
|
|
4
4
|
import {NavigationTree} from 'ydb-ui-components';
|
5
5
|
|
6
|
-
import {setCurrentSchemaPath, preloadSchemas} from '../../../../store/reducers/schema/schema';
|
7
6
|
import type {EPathType, TEvDescribeSchemeResult} from '../../../../types/api/schema';
|
7
|
+
import {setCurrentSchemaPath, preloadSchemas} from '../../../../store/reducers/schema/schema';
|
8
|
+
import {useQueryModes} from '../../../../utils/hooks';
|
8
9
|
|
9
10
|
import {isChildlessPathType, mapPathTypeToNavigationTreeType} from '../../utils/schema';
|
10
11
|
import {getActions} from '../../utils/schemaActions';
|
@@ -21,6 +22,8 @@ export function SchemaTree(props: SchemaTreeProps) {
|
|
21
22
|
|
22
23
|
const dispatch = useDispatch();
|
23
24
|
|
25
|
+
const [_, setQueryMode] = useQueryModes();
|
26
|
+
|
24
27
|
const fetchPath = (path: string) =>
|
25
28
|
window.api
|
26
29
|
.getSchema({path}, {concurrentId: `NavigationTree.getSchema|${path}`})
|
@@ -71,7 +74,10 @@ export function SchemaTree(props: SchemaTreeProps) {
|
|
71
74
|
collapsed: false,
|
72
75
|
}}
|
73
76
|
fetchPath={fetchPath}
|
74
|
-
getActions={getActions(dispatch,
|
77
|
+
getActions={getActions(dispatch, {
|
78
|
+
setActivePath: handleActivePathUpdate,
|
79
|
+
setQueryMode,
|
80
|
+
})}
|
75
81
|
activePath={currentPath}
|
76
82
|
onActivePathUpdate={handleActivePathUpdate}
|
77
83
|
cache={false}
|
@@ -6,6 +6,16 @@
|
|
6
6
|
"summary.showPreview": "Show preview",
|
7
7
|
"summary.copySchemaPath": "Copy schema path",
|
8
8
|
|
9
|
-
"actions.copied"
|
10
|
-
"actions.notCopied"
|
9
|
+
"actions.copied": "The path is copied to the clipboard",
|
10
|
+
"actions.notCopied": "Couldn’t copy the path",
|
11
|
+
"actions.externalTableSelectUnavailable": "Select query for external tables available only with 'YQL - QueryService' query mode. You need to turn in additional query modes in settings to enable it",
|
12
|
+
|
13
|
+
"actions.copyPath": "Copy path",
|
14
|
+
"actions.openPreview": "Open preview",
|
15
|
+
"actions.createTable": "Create table...",
|
16
|
+
"actions.createExternalTable": "Create external table...",
|
17
|
+
"actions.dropTable": "Drop table...",
|
18
|
+
"actions.alterTable": "Alter table...",
|
19
|
+
"actions.selectQuery": "Select query...",
|
20
|
+
"actions.upsertQuery": "Upsert query..."
|
11
21
|
}
|
@@ -7,5 +7,15 @@
|
|
7
7
|
"summary.copySchemaPath": "Скопировать путь",
|
8
8
|
|
9
9
|
"actions.copied": "Путь успешно скопирован",
|
10
|
-
"actions.notCopied": "Не получилось скопировать путь"
|
10
|
+
"actions.notCopied": "Не получилось скопировать путь",
|
11
|
+
"actions.externalTableSelectUnavailable": "Select запрос для внешних таблиц доступен только в режиме 'YQL - QueryService'. Вам необходимо включить дополнительные режимы выполнения запросов в настройках",
|
12
|
+
|
13
|
+
"actions.copyPath": "Скопировать путь",
|
14
|
+
"actions.openPreview": "Открыть превью",
|
15
|
+
"actions.createTable": "Создать таблицу...",
|
16
|
+
"actions.createExternalTable": "Создать внешнюю таблицу...",
|
17
|
+
"actions.dropTable": "Удалить таблицу...",
|
18
|
+
"actions.alterTable": "Изменить таблицу...",
|
19
|
+
"actions.selectQuery": "Select запрос...",
|
20
|
+
"actions.upsertQuery": "Upsert запрос..."
|
11
21
|
}
|
@@ -3,6 +3,8 @@ import copy from 'copy-to-clipboard';
|
|
3
3
|
|
4
4
|
import type {NavigationTreeNodeType, NavigationTreeProps} from 'ydb-ui-components';
|
5
5
|
|
6
|
+
import type {QueryMode} from '../../../types/store/query';
|
7
|
+
import type {SetQueryModeIfAvailable} from '../../../utils/hooks';
|
6
8
|
import {changeUserInput} from '../../../store/reducers/executeQuery';
|
7
9
|
import {setShowPreview} from '../../../store/reducers/schema/schema';
|
8
10
|
import {setQueryTab, setTenantPage} from '../../../store/reducers/tenant/tenant';
|
@@ -34,23 +36,63 @@ const upsertQueryTemplate = (path: string) => {
|
|
34
36
|
VALUES ( );`;
|
35
37
|
};
|
36
38
|
|
39
|
+
const dropExternalTableTemplate = (path: string) => {
|
40
|
+
return `DROP EXTERNAL TABLE \`${path}\`;`;
|
41
|
+
};
|
42
|
+
|
43
|
+
const createExternalTableTemplate = (path: string) => {
|
44
|
+
// Remove data source name from path
|
45
|
+
// to create table in the same folder with data source
|
46
|
+
const targetPath = path.split('/').slice(0, -1).join('/');
|
47
|
+
|
48
|
+
return `CREATE EXTERNAL TABLE \`${targetPath}/my_external_table\` (
|
49
|
+
column1 Int,
|
50
|
+
column2 Int
|
51
|
+
) WITH (
|
52
|
+
DATA_SOURCE="${path}",
|
53
|
+
LOCATION="",
|
54
|
+
FORMAT="json_as_string",
|
55
|
+
\`file_pattern\`=""
|
56
|
+
);`;
|
57
|
+
};
|
58
|
+
|
59
|
+
interface ActionsAdditionalEffects {
|
60
|
+
setQueryMode: SetQueryModeIfAvailable;
|
61
|
+
setActivePath: (path: string) => void;
|
62
|
+
}
|
63
|
+
|
37
64
|
const bindActions = (
|
38
65
|
path: string,
|
39
66
|
dispatch: Dispatch<any>,
|
40
|
-
|
67
|
+
additionalEffects: ActionsAdditionalEffects,
|
41
68
|
) => {
|
42
|
-
const
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
69
|
+
const {setActivePath, setQueryMode} = additionalEffects;
|
70
|
+
|
71
|
+
const inputQuery =
|
72
|
+
(tmpl: (path: string) => string, mode?: QueryMode, setQueryModeErrorMessage?: string) =>
|
73
|
+
() => {
|
74
|
+
const isNewQueryModeSet = mode && setQueryMode(mode, setQueryModeErrorMessage);
|
75
|
+
|
76
|
+
if (!mode || isNewQueryModeSet) {
|
77
|
+
dispatch(changeUserInput({input: tmpl(path)}));
|
78
|
+
dispatch(setTenantPage(TENANT_PAGES_IDS.query));
|
79
|
+
dispatch(setQueryTab(TENANT_QUERY_TABS_ID.newQuery));
|
80
|
+
setActivePath(path);
|
81
|
+
}
|
82
|
+
};
|
48
83
|
|
49
84
|
return {
|
50
|
-
createTable: inputQuery(createTableTemplate),
|
51
|
-
alterTable: inputQuery(alterTableTemplate),
|
85
|
+
createTable: inputQuery(createTableTemplate, 'script'),
|
86
|
+
alterTable: inputQuery(alterTableTemplate, 'script'),
|
52
87
|
selectQuery: inputQuery(selectQueryTemplate),
|
53
88
|
upsertQuery: inputQuery(upsertQueryTemplate),
|
89
|
+
createExternalTable: inputQuery(createExternalTableTemplate, 'script'),
|
90
|
+
dropExternalTable: inputQuery(dropExternalTableTemplate, 'script'),
|
91
|
+
selectQueryFromExternalTable: inputQuery(
|
92
|
+
selectQueryTemplate,
|
93
|
+
'query',
|
94
|
+
i18n('actions.externalTableSelectUnavailable'),
|
95
|
+
),
|
54
96
|
copyPath: () => {
|
55
97
|
try {
|
56
98
|
copy(path);
|
@@ -79,27 +121,40 @@ const bindActions = (
|
|
79
121
|
type ActionsSet = ReturnType<Required<NavigationTreeProps>['getActions']>;
|
80
122
|
|
81
123
|
export const getActions =
|
82
|
-
(dispatch: Dispatch<any>,
|
124
|
+
(dispatch: Dispatch<any>, additionalEffects: ActionsAdditionalEffects) =>
|
83
125
|
(path: string, type: NavigationTreeNodeType) => {
|
84
|
-
const actions = bindActions(path, dispatch,
|
85
|
-
const copyItem = {text: '
|
86
|
-
const openPreview = {text: '
|
87
|
-
const selectQuery = {text: 'Select query...', action: actions.selectQuery};
|
126
|
+
const actions = bindActions(path, dispatch, additionalEffects);
|
127
|
+
const copyItem = {text: i18n('actions.copyPath'), action: actions.copyPath};
|
128
|
+
const openPreview = {text: i18n('actions.openPreview'), action: actions.openPreview};
|
88
129
|
|
89
130
|
const DIR_SET: ActionsSet = [
|
90
131
|
[copyItem],
|
91
|
-
[{text: '
|
132
|
+
[{text: i18n('actions.createTable'), action: actions.createTable}],
|
92
133
|
];
|
93
134
|
const TABLE_SET: ActionsSet = [
|
94
135
|
[openPreview, copyItem],
|
95
136
|
[
|
96
|
-
{text: '
|
97
|
-
selectQuery,
|
98
|
-
{text: '
|
137
|
+
{text: i18n('actions.alterTable'), action: actions.alterTable},
|
138
|
+
{text: i18n('actions.selectQuery'), action: actions.selectQuery},
|
139
|
+
{text: i18n('actions.upsertQuery'), action: actions.upsertQuery},
|
140
|
+
],
|
141
|
+
];
|
142
|
+
|
143
|
+
const EXTERNAL_TABLE_SET = [
|
144
|
+
[openPreview, copyItem],
|
145
|
+
[
|
146
|
+
{
|
147
|
+
text: i18n('actions.selectQuery'),
|
148
|
+
action: actions.selectQueryFromExternalTable,
|
149
|
+
},
|
99
150
|
],
|
151
|
+
[{text: i18n('actions.dropTable'), action: actions.dropExternalTable}],
|
100
152
|
];
|
101
153
|
|
102
|
-
const
|
154
|
+
const EXTERNAL_DATA_SOURCE_SET = [
|
155
|
+
[copyItem],
|
156
|
+
[{text: i18n('actions.createExternalTable'), action: actions.createExternalTable}],
|
157
|
+
];
|
103
158
|
|
104
159
|
const JUST_COPY: ActionsSet = [copyItem];
|
105
160
|
|
@@ -118,7 +173,7 @@ export const getActions =
|
|
118
173
|
index: JUST_COPY,
|
119
174
|
|
120
175
|
external_table: EXTERNAL_TABLE_SET,
|
121
|
-
external_data_source:
|
176
|
+
external_data_source: EXTERNAL_DATA_SOURCE_SET,
|
122
177
|
};
|
123
178
|
|
124
179
|
return nodeTypeToActions[type];
|
package/dist/index.js
CHANGED
@@ -1,13 +1,17 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import ReactDOM from 'react-dom';
|
3
|
-
import './index.css';
|
4
|
-
import App from './containers/App/App';
|
5
3
|
import {Provider} from 'react-redux';
|
4
|
+
|
5
|
+
import '@gravity-ui/uikit/styles/styles.scss';
|
6
|
+
|
7
|
+
import App from './containers/App/App';
|
6
8
|
import configureStore from './store';
|
7
9
|
import reportWebVitals from './reportWebVitals';
|
8
|
-
import '@gravity-ui/uikit/styles/styles.scss';
|
9
10
|
import HistoryContext from './contexts/HistoryContext';
|
10
11
|
|
12
|
+
import './styles/constants.scss';
|
13
|
+
import './index.css';
|
14
|
+
|
11
15
|
const {store, history} = configureStore();
|
12
16
|
window.store = store;
|
13
17
|
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import {i18n, Lang} from '../../../utils/i18n';
|
2
|
+
|
3
|
+
import en from './en.json';
|
4
|
+
import ru from './ru.json';
|
5
|
+
|
6
|
+
const COMPONENT = 'ydb-hooks';
|
7
|
+
|
8
|
+
i18n.registerKeyset(Lang.En, COMPONENT, en);
|
9
|
+
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
|
10
|
+
|
11
|
+
export default i18n.keyset(COMPONENT);
|
@@ -0,0 +1,34 @@
|
|
1
|
+
import type {QueryMode} from '../../types/store/query';
|
2
|
+
import {ENABLE_ADDITIONAL_QUERY_MODES, QUERY_INITIAL_MODE_KEY} from '../constants';
|
3
|
+
import {isNewQueryMode} from '../query';
|
4
|
+
import createToast from '../createToast';
|
5
|
+
import {useSetting} from './useSetting';
|
6
|
+
import i18n from './i18n';
|
7
|
+
|
8
|
+
export type SetQueryModeIfAvailable = (
|
9
|
+
value: QueryMode,
|
10
|
+
errorMessage?: string | undefined,
|
11
|
+
) => boolean;
|
12
|
+
|
13
|
+
export const useQueryModes = (): [QueryMode, SetQueryModeIfAvailable] => {
|
14
|
+
const [queryMode, setQueryMode] = useSetting<QueryMode>(QUERY_INITIAL_MODE_KEY);
|
15
|
+
const [enableAdditionalQueryModes] = useSetting<boolean>(ENABLE_ADDITIONAL_QUERY_MODES);
|
16
|
+
|
17
|
+
const setQueryModeIfAvailable: SetQueryModeIfAvailable = (value, errorMessage) => {
|
18
|
+
if (isNewQueryMode(value) && !enableAdditionalQueryModes) {
|
19
|
+
createToast({
|
20
|
+
name: 'QueryModeCannotBeSet',
|
21
|
+
title: errorMessage ?? i18n('useQueryModes.queryModeCannotBeSet', {mode: value}),
|
22
|
+
type: 'error',
|
23
|
+
});
|
24
|
+
|
25
|
+
return false;
|
26
|
+
} else {
|
27
|
+
setQueryMode(value);
|
28
|
+
|
29
|
+
return true;
|
30
|
+
}
|
31
|
+
};
|
32
|
+
|
33
|
+
return [queryMode, setQueryModeIfAvailable];
|
34
|
+
};
|
package/dist/utils/query.ts
CHANGED
@@ -7,7 +7,7 @@ import type {
|
|
7
7
|
QueryPlan,
|
8
8
|
ScriptPlan,
|
9
9
|
} from '../types/api/query';
|
10
|
-
import type {IQueryResult, QueryErrorResponse} from '../types/store/query';
|
10
|
+
import type {IQueryResult, QueryErrorResponse, QueryMode} from '../types/store/query';
|
11
11
|
|
12
12
|
export const QUERY_ACTIONS = {
|
13
13
|
execute: 'execute',
|
@@ -21,6 +21,10 @@ export const QUERY_MODES = {
|
|
21
21
|
query: 'query',
|
22
22
|
} as const;
|
23
23
|
|
24
|
+
export const isNewQueryMode = (value: QueryMode) => {
|
25
|
+
return value !== QUERY_MODES.script && value !== QUERY_MODES.scan;
|
26
|
+
};
|
27
|
+
|
24
28
|
// eslint-disable-next-line complexity
|
25
29
|
export const getColumnType = (type: string) => {
|
26
30
|
switch (type.replace(/\?$/, '')) {
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "ydb-embedded-ui",
|
3
|
-
"version": "4.
|
3
|
+
"version": "4.13.0",
|
4
4
|
"files": [
|
5
5
|
"dist"
|
6
6
|
],
|
@@ -40,7 +40,7 @@
|
|
40
40
|
"reselect": "4.1.6",
|
41
41
|
"sass": "1.32.8",
|
42
42
|
"web-vitals": "1.1.2",
|
43
|
-
"ydb-ui-components": "^3.2.
|
43
|
+
"ydb-ui-components": "^3.2.2"
|
44
44
|
},
|
45
45
|
"scripts": {
|
46
46
|
"start": "react-app-rewired start",
|