ydb-embedded-ui 4.12.0 → 4.14.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/components/InfoViewer/formatters/common.ts +4 -2
  3. package/dist/components/InfoViewer/i18n/en.json +4 -0
  4. package/dist/components/InfoViewer/i18n/index.ts +11 -0
  5. package/dist/components/InfoViewer/i18n/ru.json +4 -0
  6. package/dist/components/Tablet/Tablet.scss +1 -16
  7. package/dist/components/Tablet/Tablet.tsx +5 -5
  8. package/dist/components/TabletIcon/TabletIcon.scss +17 -0
  9. package/dist/components/TabletIcon/TabletIcon.tsx +18 -0
  10. package/dist/containers/Header/Header.scss +2 -0
  11. package/dist/containers/Header/Header.tsx +2 -7
  12. package/dist/containers/Header/{breadcrumbs.ts → breadcrumbs.tsx} +19 -8
  13. package/dist/containers/Nodes/Nodes.tsx +53 -16
  14. package/dist/containers/Nodes/getNodesColumns.tsx +31 -13
  15. package/dist/containers/Tablet/Tablet.tsx +9 -3
  16. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +5 -2
  17. package/dist/containers/Tenant/Info/ExternalDataSource/ExternalDataSource.scss +5 -0
  18. package/dist/containers/Tenant/Info/ExternalDataSource/ExternalDataSource.tsx +81 -0
  19. package/dist/containers/Tenant/Info/ExternalTable/ExternalTable.scss +5 -0
  20. package/dist/containers/Tenant/Info/ExternalTable/ExternalTable.tsx +103 -0
  21. package/dist/containers/Tenant/Info/i18n/en.json +8 -0
  22. package/dist/containers/Tenant/Info/i18n/index.ts +11 -0
  23. package/dist/containers/Tenant/Info/i18n/ru.json +8 -0
  24. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.scss +4 -4
  25. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +10 -3
  26. package/dist/containers/Tenant/Query/QueryDuration/QueryDuration.scss +8 -0
  27. package/dist/containers/Tenant/Query/QueryDuration/QueryDuration.tsx +13 -1
  28. package/dist/containers/Tenant/Query/QueryEditor/QueryEditor.js +4 -6
  29. package/dist/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.scss +3 -1
  30. package/dist/containers/Tenant/Query/i18n/en.json +6 -4
  31. package/dist/containers/Tenant/Query/i18n/ru.json +6 -4
  32. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +12 -2
  33. package/dist/containers/Tenant/i18n/en.json +12 -2
  34. package/dist/containers/Tenant/i18n/ru.json +11 -1
  35. package/dist/containers/Tenant/utils/schemaActions.ts +76 -28
  36. package/dist/containers/Tenant/utils/schemaControls.tsx +69 -0
  37. package/dist/containers/UserSettings/i18n/en.json +3 -0
  38. package/dist/containers/UserSettings/i18n/ru.json +3 -0
  39. package/dist/containers/UserSettings/settings.ts +12 -1
  40. package/dist/index.js +7 -3
  41. package/dist/services/api.ts +24 -12
  42. package/dist/store/reducers/header/types.ts +2 -0
  43. package/dist/store/reducers/nodes/nodes.ts +23 -6
  44. package/dist/store/reducers/nodes/selectors.ts +2 -2
  45. package/dist/store/reducers/nodes/types.ts +15 -5
  46. package/dist/store/reducers/settings/settings.ts +5 -0
  47. package/dist/styles/constants.scss +3 -0
  48. package/dist/types/api/compute.ts +0 -12
  49. package/dist/types/api/nodes.ts +0 -12
  50. package/dist/utils/constants.ts +3 -0
  51. package/dist/utils/filters.ts +23 -0
  52. package/dist/utils/hooks/i18n/en.json +3 -0
  53. package/dist/utils/hooks/i18n/index.ts +11 -0
  54. package/dist/utils/hooks/i18n/ru.json +3 -0
  55. package/dist/utils/hooks/index.ts +4 -0
  56. package/dist/utils/hooks/useNodesRequestParams.ts +46 -0
  57. package/dist/utils/hooks/useQueryModes.ts +34 -0
  58. package/dist/utils/hooks/useTableSort.ts +37 -0
  59. package/dist/utils/nodes.ts +25 -0
  60. package/dist/utils/query.ts +5 -1
  61. package/package.json +2 -2
@@ -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,5 @@
1
+ .ydb-external-table-info {
2
+ &__location {
3
+ max-width: var(--tenant-object-info-max-value-width);
4
+ }
5
+ }
@@ -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]: undefined,
201
- [EPathType.EPathTypeExternalDataSource]: undefined,
203
+ [EPathType.EPathTypeExternalTable]: () => (
204
+ <ExternalTableSummary data={currentObjectData} />
205
+ ),
206
+ [EPathType.EPathTypeExternalDataSource]: () => (
207
+ <ExternalDataSourceSummary data={currentObjectData} />
208
+ ),
202
209
  };
203
210
 
204
211
  let component =
@@ -5,4 +5,12 @@
5
5
  margin-left: 10px;
6
6
 
7
7
  color: var(--yc-color-text-complementary);
8
+
9
+ &__item-with-popover {
10
+ white-space: nowrap;
11
+ }
12
+
13
+ &__popover {
14
+ max-width: 300px;
15
+ }
8
16
  }
@@ -1,6 +1,9 @@
1
1
  import block from 'bem-cn-lite';
2
2
 
3
3
  import {formatDurationToShortTimeFormat, parseUsToMs} from '../../../../utils/timeParsers';
4
+ import {LabelWithPopover} from '../../../../components/LabelWithPopover';
5
+
6
+ import i18n from '../i18n';
4
7
 
5
8
  import './QueryDuration.scss';
6
9
 
@@ -17,5 +20,14 @@ export const QueryDuration = ({duration}: QueryDurationProps) => {
17
20
 
18
21
  const parsedDuration = formatDurationToShortTimeFormat(parseUsToMs(duration), 1);
19
22
 
20
- return <span className={b()}>{parsedDuration}</span>;
23
+ return (
24
+ <span className={b()}>
25
+ <LabelWithPopover
26
+ className={b('item-with-popover')}
27
+ contentClassName={b('popover')}
28
+ text={parsedDuration}
29
+ popoverContent={i18n('query-duration.description')}
30
+ />
31
+ </span>
32
+ );
21
33
  };
@@ -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] = useSetting(QUERY_INITIAL_MODE_KEY);
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
- const isNewQueryMode = queryMode !== QUERY_MODES.script && queryMode !== QUERY_MODES.scan;
100
- if (!enableAdditionalQueryModes && isNewQueryMode) {
98
+ if (isNewQueryMode(queryMode) && !enableAdditionalQueryModes) {
101
99
  setQueryMode(QUERY_MODES.script);
102
100
  }
103
101
  }, [enableAdditionalQueryModes, queryMode, setQueryMode]);
@@ -67,6 +67,8 @@
67
67
  }
68
68
 
69
69
  &__popover {
70
- max-width: 340px;
70
+ max-width: 420px;
71
+
72
+ white-space: pre-wrap;
71
73
  }
72
74
  }
@@ -17,8 +17,10 @@
17
17
  "preview.not-available": "Preview is not available",
18
18
  "preview.close": "Close preview",
19
19
 
20
- "method-description.script": "For YQL-scripts combining DDL and DML. API call: schema.scripting",
21
- "method-description.scan": "Read-only queries, potentially reading a lot of data. API call: table.ExecuteScan",
22
- "method-description.data": "DML queries for changing and fetching data in serialization mode. API call: table.executeDataQuery",
23
- "method-description.query": "Any queries. An experimental API call supposed to replace all existing methods. API Call: query.ExecuteScript"
20
+ "method-description.script": "For YQL-scripts combining DDL and DML.\nAPI call: schema.scripting",
21
+ "method-description.scan": "Read-only queries, potentially reading a lot of data.\nAPI call: table.ExecuteScan",
22
+ "method-description.data": "DML queries for changing and fetching data in serialization mode.\nAPI call: table.executeDataQuery",
23
+ "method-description.query": "Any query. An experimental API call supposed to replace all existing methods.\nAPI Call: query.ExecuteScript",
24
+
25
+ "query-duration.description": "Duration of server-side query execution"
24
26
  }
@@ -17,8 +17,10 @@
17
17
  "preview.not-available": "Предпросмотр недоступен",
18
18
  "preview.close": "Закрыть предпросмотр",
19
19
 
20
- "method-description.script": "Для скриптов, совмещающих DDL и DML-конструкции. API call: schema.scripting",
21
- "method-description.scan": "Только читающие запросы, потенциально читающие много данных. API call: table.ExecuteScan",
22
- "method-description.data": "DML-запросы для изменения и выборки данных в режиме изоляции Serializable. API call: table.executeDataQuery",
23
- "method-description.query": "Любые запросы. Экспериментальный перспективный метод, который в будущем заменит все остальные. API call: query.ExecuteScript"
20
+ "method-description.script": "Для скриптов, совмещающих DDL и DML-конструкции.\nAPI call: schema.scripting",
21
+ "method-description.scan": "Только читающие запросы, потенциально читающие много данных.\nAPI call: table.ExecuteScan",
22
+ "method-description.data": "DML-запросы для изменения и выборки данных в режиме изоляции Serializable.\nAPI call: table.executeDataQuery",
23
+ "method-description.query": "Любые запросы. Экспериментальный перспективный метод, который в будущем заменит все остальные.\nAPI call: query.ExecuteScript",
24
+
25
+ "query-duration.description": "Время выполнения запроса на стороне сервера"
24
26
  }
@@ -3,11 +3,13 @@ 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';
12
+ import {getControls} from '../../utils/schemaControls';
11
13
 
12
14
  interface SchemaTreeProps {
13
15
  rootPath: string;
@@ -21,6 +23,8 @@ export function SchemaTree(props: SchemaTreeProps) {
21
23
 
22
24
  const dispatch = useDispatch();
23
25
 
26
+ const [_, setQueryMode] = useQueryModes();
27
+
24
28
  const fetchPath = (path: string) =>
25
29
  window.api
26
30
  .getSchema({path}, {concurrentId: `NavigationTree.getSchema|${path}`})
@@ -71,7 +75,13 @@ export function SchemaTree(props: SchemaTreeProps) {
71
75
  collapsed: false,
72
76
  }}
73
77
  fetchPath={fetchPath}
74
- getActions={getActions(dispatch, handleActivePathUpdate)}
78
+ getActions={getActions(dispatch, {
79
+ setActivePath: handleActivePathUpdate,
80
+ setQueryMode,
81
+ })}
82
+ renderAdditionalNodeElements={getControls(dispatch, {
83
+ setActivePath: handleActivePathUpdate,
84
+ })}
75
85
  activePath={currentPath}
76
86
  onActivePathUpdate={handleActivePathUpdate}
77
87
  cache={false}
@@ -6,6 +6,16 @@
6
6
  "summary.showPreview": "Show preview",
7
7
  "summary.copySchemaPath": "Copy schema path",
8
8
 
9
- "actions.copied" : "The path is copied to the clipboard",
10
- "actions.notCopied" : "Couldn’t copy the path"
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,8 +3,9 @@ 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
- import {setShowPreview} from '../../../store/reducers/schema/schema';
8
9
  import {setQueryTab, setTenantPage} from '../../../store/reducers/tenant/tenant';
9
10
  import {TENANT_QUERY_TABS_ID, TENANT_PAGES_IDS} from '../../../store/reducers/tenant/constants';
10
11
  import createToast from '../../../utils/createToast';
@@ -34,23 +35,63 @@ const upsertQueryTemplate = (path: string) => {
34
35
  VALUES ( );`;
35
36
  };
36
37
 
38
+ const dropExternalTableTemplate = (path: string) => {
39
+ return `DROP EXTERNAL TABLE \`${path}\`;`;
40
+ };
41
+
42
+ const createExternalTableTemplate = (path: string) => {
43
+ // Remove data source name from path
44
+ // to create table in the same folder with data source
45
+ const targetPath = path.split('/').slice(0, -1).join('/');
46
+
47
+ return `CREATE EXTERNAL TABLE \`${targetPath}/my_external_table\` (
48
+ column1 Int,
49
+ column2 Int
50
+ ) WITH (
51
+ DATA_SOURCE="${path}",
52
+ LOCATION="",
53
+ FORMAT="json_as_string",
54
+ \`file_pattern\`=""
55
+ );`;
56
+ };
57
+
58
+ interface ActionsAdditionalEffects {
59
+ setQueryMode: SetQueryModeIfAvailable;
60
+ setActivePath: (path: string) => void;
61
+ }
62
+
37
63
  const bindActions = (
38
64
  path: string,
39
65
  dispatch: Dispatch<any>,
40
- setActivePath: (path: string) => void,
66
+ additionalEffects: ActionsAdditionalEffects,
41
67
  ) => {
42
- const inputQuery = (tmpl: (path: string) => string) => () => {
43
- dispatch(changeUserInput({input: tmpl(path)}));
44
- dispatch(setTenantPage(TENANT_PAGES_IDS.query));
45
- dispatch(setQueryTab(TENANT_QUERY_TABS_ID.newQuery));
46
- setActivePath(path);
47
- };
68
+ const {setActivePath, setQueryMode} = additionalEffects;
69
+
70
+ const inputQuery =
71
+ (tmpl: (path: string) => string, mode?: QueryMode, setQueryModeErrorMessage?: string) =>
72
+ () => {
73
+ const isNewQueryModeSet = mode && setQueryMode(mode, setQueryModeErrorMessage);
74
+
75
+ if (!mode || isNewQueryModeSet) {
76
+ dispatch(changeUserInput({input: tmpl(path)}));
77
+ dispatch(setTenantPage(TENANT_PAGES_IDS.query));
78
+ dispatch(setQueryTab(TENANT_QUERY_TABS_ID.newQuery));
79
+ setActivePath(path);
80
+ }
81
+ };
48
82
 
49
83
  return {
50
- createTable: inputQuery(createTableTemplate),
51
- alterTable: inputQuery(alterTableTemplate),
84
+ createTable: inputQuery(createTableTemplate, 'script'),
85
+ alterTable: inputQuery(alterTableTemplate, 'script'),
52
86
  selectQuery: inputQuery(selectQueryTemplate),
53
87
  upsertQuery: inputQuery(upsertQueryTemplate),
88
+ createExternalTable: inputQuery(createExternalTableTemplate, 'script'),
89
+ dropExternalTable: inputQuery(dropExternalTableTemplate, 'script'),
90
+ selectQueryFromExternalTable: inputQuery(
91
+ selectQueryTemplate,
92
+ 'query',
93
+ i18n('actions.externalTableSelectUnavailable'),
94
+ ),
54
95
  copyPath: () => {
55
96
  try {
56
97
  copy(path);
@@ -67,39 +108,45 @@ const bindActions = (
67
108
  });
68
109
  }
69
110
  },
70
- openPreview: () => {
71
- dispatch(setShowPreview(true));
72
- dispatch(setTenantPage(TENANT_PAGES_IDS.query));
73
- dispatch(setQueryTab(TENANT_QUERY_TABS_ID.newQuery));
74
- setActivePath(path);
75
- },
76
111
  };
77
112
  };
78
113
 
79
114
  type ActionsSet = ReturnType<Required<NavigationTreeProps>['getActions']>;
80
115
 
81
116
  export const getActions =
82
- (dispatch: Dispatch<any>, setActivePath: (path: string) => void) =>
117
+ (dispatch: Dispatch<any>, additionalEffects: ActionsAdditionalEffects) =>
83
118
  (path: string, type: NavigationTreeNodeType) => {
84
- const actions = bindActions(path, dispatch, setActivePath);
85
- const copyItem = {text: 'Copy path', action: actions.copyPath};
86
- const openPreview = {text: 'Open preview', action: actions.openPreview};
87
- const selectQuery = {text: 'Select query...', action: actions.selectQuery};
119
+ const actions = bindActions(path, dispatch, additionalEffects);
120
+ const copyItem = {text: i18n('actions.copyPath'), action: actions.copyPath};
88
121
 
89
122
  const DIR_SET: ActionsSet = [
90
123
  [copyItem],
91
- [{text: 'Create table...', action: actions.createTable}],
124
+ [{text: i18n('actions.createTable'), action: actions.createTable}],
92
125
  ];
93
126
  const TABLE_SET: ActionsSet = [
94
- [openPreview, copyItem],
127
+ [copyItem],
95
128
  [
96
- {text: 'Alter table...', action: actions.alterTable},
97
- selectQuery,
98
- {text: 'Upsert query...', action: actions.upsertQuery},
129
+ {text: i18n('actions.alterTable'), action: actions.alterTable},
130
+ {text: i18n('actions.selectQuery'), action: actions.selectQuery},
131
+ {text: i18n('actions.upsertQuery'), action: actions.upsertQuery},
99
132
  ],
100
133
  ];
101
134
 
102
- const EXTERNAL_TABLE_SET = [[openPreview, copyItem], [selectQuery]];
135
+ const EXTERNAL_TABLE_SET = [
136
+ [copyItem],
137
+ [
138
+ {
139
+ text: i18n('actions.selectQuery'),
140
+ action: actions.selectQueryFromExternalTable,
141
+ },
142
+ ],
143
+ [{text: i18n('actions.dropTable'), action: actions.dropExternalTable}],
144
+ ];
145
+
146
+ const EXTERNAL_DATA_SOURCE_SET = [
147
+ [copyItem],
148
+ [{text: i18n('actions.createExternalTable'), action: actions.createExternalTable}],
149
+ ];
103
150
 
104
151
  const JUST_COPY: ActionsSet = [copyItem];
105
152
 
@@ -114,11 +161,12 @@ export const getActions =
114
161
 
115
162
  index_table: JUST_COPY,
116
163
  topic: JUST_COPY,
164
+ stream: JUST_COPY,
117
165
 
118
166
  index: JUST_COPY,
119
167
 
120
168
  external_table: EXTERNAL_TABLE_SET,
121
- external_data_source: JUST_COPY,
169
+ external_data_source: EXTERNAL_DATA_SOURCE_SET,
122
170
  };
123
171
 
124
172
  return nodeTypeToActions[type];