ydb-embedded-ui 22.2.0 → 22.3.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 (116) hide show
  1. package/dist/components/Graph/GravityGraph.js +9 -2
  2. package/dist/components/Graph/GravityGraph.js.map +1 -1
  3. package/dist/components/Graph/utils.d.ts +2 -1
  4. package/dist/components/Graph/utils.js +7 -0
  5. package/dist/components/Graph/utils.js.map +1 -1
  6. package/dist/components/PaginatedTable/rowLookup.js +30 -9
  7. package/dist/components/PaginatedTable/rowLookup.js.map +1 -1
  8. package/dist/components/TableKeyboardNavigation/TableKeyboardNavigation.js +5 -1
  9. package/dist/components/TableKeyboardNavigation/TableKeyboardNavigation.js.map +1 -1
  10. package/dist/containers/Authentication/Authentication.js +29 -30
  11. package/dist/containers/Authentication/Authentication.js.map +1 -1
  12. package/dist/containers/Authentication/utils.d.ts +2 -2
  13. package/dist/containers/Authentication/utils.js +1 -1
  14. package/dist/containers/Authentication/utils.js.map +1 -1
  15. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.js +5 -1
  16. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.js.map +1 -1
  17. package/dist/containers/Tenant/Diagnostics/Overview/Overview.js +7 -1
  18. package/dist/containers/Tenant/Diagnostics/Overview/Overview.js.map +1 -1
  19. package/dist/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/ResourcePoolInfo.scss +24 -0
  20. package/dist/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/ResourcePoolUsage.d.ts +8 -0
  21. package/dist/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/ResourcePoolUsage.js +34 -0
  22. package/dist/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/ResourcePoolUsage.js.map +1 -0
  23. package/dist/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/i18n/en.json +4 -1
  24. package/dist/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/i18n/index.d.ts +1 -1
  25. package/dist/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/index.d.ts +1 -0
  26. package/dist/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/index.js +1 -0
  27. package/dist/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/index.js.map +1 -1
  28. package/dist/containers/Tenant/Diagnostics/Overview/SchemaObjectInfo/SchemaObjectInfo.js +1 -1
  29. package/dist/containers/Tenant/Diagnostics/Overview/SchemaObjectInfo/SchemaObjectInfo.js.map +1 -1
  30. package/dist/containers/Tenant/Diagnostics/Overview/StreamingQueryInfo/StreamingQueryInfo.js +2 -17
  31. package/dist/containers/Tenant/Diagnostics/Overview/StreamingQueryInfo/StreamingQueryInfo.js.map +1 -1
  32. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/en.json +4 -0
  33. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/index.d.ts +1 -1
  34. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo/prepareColumnTableInfo.js +4 -1
  35. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo/prepareColumnTableInfo.js.map +1 -1
  36. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo/prepareRowTableInfo.js +4 -1
  37. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo/prepareRowTableInfo.js.map +1 -1
  38. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo/prepareTTL.js +41 -3
  39. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo/prepareTTL.js.map +1 -1
  40. package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/StreamingQueryGraph.d.ts +7 -0
  41. package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/StreamingQueryGraph.js +38 -0
  42. package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/StreamingQueryGraph.js.map +1 -0
  43. package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/StreamingQueryGraph.scss +18 -0
  44. package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/i18n/en.json +5 -0
  45. package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/i18n/index.d.ts +2 -0
  46. package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/i18n/index.js +5 -0
  47. package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/i18n/index.js.map +1 -0
  48. package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/index.d.ts +1 -0
  49. package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/index.js +2 -0
  50. package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/index.js.map +1 -0
  51. package/dist/containers/Tenant/Diagnostics/i18n/en.json +1 -0
  52. package/dist/containers/Tenant/Diagnostics/i18n/index.d.ts +1 -1
  53. package/dist/containers/Tenant/Diagnostics/tabsContent.js +2 -0
  54. package/dist/containers/Tenant/Diagnostics/tabsContent.js.map +1 -1
  55. package/dist/containers/Tenant/Query/QueryEditor/YqlEditor/YqlEditor.js +8 -1
  56. package/dist/containers/Tenant/Query/QueryEditor/YqlEditor/YqlEditor.js.map +1 -1
  57. package/dist/store/reducers/executeTopQueries/executeTopQueries.d.ts +7 -0
  58. package/dist/store/reducers/executeTopQueries/executeTopQueries.js +38 -0
  59. package/dist/store/reducers/executeTopQueries/executeTopQueries.js.map +1 -1
  60. package/dist/store/reducers/query/parsers/prepareStreamingQueryPlan.d.ts +8 -0
  61. package/dist/store/reducers/query/parsers/prepareStreamingQueryPlan.js +53 -0
  62. package/dist/store/reducers/query/parsers/prepareStreamingQueryPlan.js.map +1 -0
  63. package/dist/store/reducers/streamingQuery/streamingQuery.d.ts +5 -1
  64. package/dist/store/reducers/streamingQuery/streamingQuery.js +35 -10
  65. package/dist/store/reducers/streamingQuery/streamingQuery.js.map +1 -1
  66. package/dist/store/reducers/tenant/constants.d.ts +1 -0
  67. package/dist/store/reducers/tenant/constants.js +1 -0
  68. package/dist/store/reducers/tenant/constants.js.map +1 -1
  69. package/dist/types/api/schema/columnEntity.d.ts +2 -1
  70. package/dist/types/api/schema/sysView.d.ts +59 -2
  71. package/dist/types/api/schema/sysView.js +53 -1
  72. package/dist/types/api/schema/sysView.js.map +1 -1
  73. package/dist/types/api/schema/table.d.ts +8 -0
  74. package/dist/types/api/schema/table.js.map +1 -1
  75. package/dist/utils/query.d.ts +1 -0
  76. package/dist/utils/query.js +40 -0
  77. package/dist/utils/query.js.map +1 -1
  78. package/dist/utils/schema.d.ts +6 -2
  79. package/dist/utils/schema.js +14 -4
  80. package/dist/utils/schema.js.map +1 -1
  81. package/package.json +1 -1
  82. package/src/components/Graph/GravityGraph.tsx +9 -2
  83. package/src/components/Graph/utils.ts +9 -1
  84. package/src/components/PaginatedTable/rowLookup.ts +35 -10
  85. package/src/components/TableKeyboardNavigation/TableKeyboardNavigation.tsx +5 -1
  86. package/src/containers/Authentication/Authentication.tsx +29 -34
  87. package/src/containers/Authentication/utils.ts +3 -3
  88. package/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts +6 -1
  89. package/src/containers/Tenant/Diagnostics/Overview/Overview.tsx +17 -1
  90. package/src/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/ResourcePoolInfo.scss +24 -0
  91. package/src/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/ResourcePoolUsage.tsx +53 -0
  92. package/src/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/i18n/en.json +4 -1
  93. package/src/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/index.ts +1 -0
  94. package/src/containers/Tenant/Diagnostics/Overview/SchemaObjectInfo/SchemaObjectInfo.tsx +7 -1
  95. package/src/containers/Tenant/Diagnostics/Overview/StreamingQueryInfo/StreamingQueryInfo.tsx +2 -20
  96. package/src/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/en.json +4 -0
  97. package/src/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo/prepareColumnTableInfo.tsx +11 -1
  98. package/src/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo/prepareRowTableInfo.tsx +11 -1
  99. package/src/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo/prepareTTL.ts +45 -3
  100. package/src/containers/Tenant/Diagnostics/StreamingQueryGraph/StreamingQueryGraph.scss +18 -0
  101. package/src/containers/Tenant/Diagnostics/StreamingQueryGraph/StreamingQueryGraph.tsx +79 -0
  102. package/src/containers/Tenant/Diagnostics/StreamingQueryGraph/i18n/en.json +5 -0
  103. package/src/containers/Tenant/Diagnostics/StreamingQueryGraph/i18n/index.ts +7 -0
  104. package/src/containers/Tenant/Diagnostics/StreamingQueryGraph/index.ts +1 -0
  105. package/src/containers/Tenant/Diagnostics/i18n/en.json +1 -0
  106. package/src/containers/Tenant/Diagnostics/tabsContent.tsx +4 -0
  107. package/src/containers/Tenant/Query/QueryEditor/YqlEditor/YqlEditor.tsx +8 -1
  108. package/src/store/reducers/executeTopQueries/executeTopQueries.ts +46 -0
  109. package/src/store/reducers/query/parsers/prepareStreamingQueryPlan.ts +74 -0
  110. package/src/store/reducers/streamingQuery/streamingQuery.ts +43 -16
  111. package/src/store/reducers/tenant/constants.ts +1 -0
  112. package/src/types/api/schema/columnEntity.ts +2 -1
  113. package/src/types/api/schema/sysView.ts +60 -37
  114. package/src/types/api/schema/table.ts +7 -0
  115. package/src/utils/query.ts +55 -0
  116. package/src/utils/schema.ts +22 -5
@@ -0,0 +1,79 @@
1
+ import React from 'react';
2
+
3
+ import {Text, useThemeValue} from '@gravity-ui/uikit';
4
+
5
+ import {ResponseError} from '../../../../components/Errors/ResponseError';
6
+ import {Loader} from '../../../../components/Loader';
7
+ import {prepareStreamingQueryPlan} from '../../../../store/reducers/query/parsers/prepareStreamingQueryPlan';
8
+ import {streamingQueriesApi} from '../../../../store/reducers/streamingQuery/streamingQuery';
9
+ import {cn} from '../../../../utils/cn';
10
+ import {getStringifiedData} from '../../../../utils/dataFormatters/dataFormatters';
11
+ import {useAutoRefreshInterval} from '../../../../utils/hooks';
12
+ import {parseIssuesData} from '../../../../utils/query';
13
+ import {ResultIssues} from '../../Query/Issues/Issues';
14
+ import {Graph} from '../../Query/QueryResult/components/Graph/Graph';
15
+
16
+ import i18n from './i18n';
17
+
18
+ import './StreamingQueryGraph.scss';
19
+
20
+ interface StreamingQueryGraphProps {
21
+ database: string;
22
+ path: string;
23
+ }
24
+
25
+ const RUNNING_STATUS = 'RUNNING';
26
+
27
+ const b = cn('ydb-streaming-query-graph');
28
+
29
+ const PLAN_STATE_MESSAGES = {
30
+ empty: 'description_no-plan',
31
+ unparsed: 'description_unparsed-plan',
32
+ unsupported: 'description_unsupported-plan',
33
+ } as const;
34
+
35
+ export function StreamingQueryGraph({database, path}: StreamingQueryGraphProps) {
36
+ const theme = useThemeValue();
37
+ const [autoRefreshInterval] = useAutoRefreshInterval();
38
+
39
+ const {
40
+ currentData: planData,
41
+ isFetching,
42
+ error,
43
+ } = streamingQueriesApi.useGetStreamingQueryPlanQuery(
44
+ {database, path},
45
+ {skip: !database || !path, pollingInterval: autoRefreshInterval},
46
+ );
47
+
48
+ const row = planData?.resultSets?.[0]?.result?.[0];
49
+
50
+ const {state, prepared} = React.useMemo(
51
+ () => prepareStreamingQueryPlan(getStringifiedData(row?.Plan)),
52
+ [row?.Plan],
53
+ );
54
+
55
+ if (isFetching && planData === undefined) {
56
+ return <Loader size="s" className={b('loader')} />;
57
+ }
58
+
59
+ const status = getStringifiedData(row?.Status);
60
+ const issues = parseIssuesData(row?.Issues);
61
+ const showStatus = Boolean(issues) || Boolean(status && status !== RUNNING_STATUS);
62
+
63
+ return (
64
+ <div className={b()}>
65
+ {error ? <ResponseError error={error} /> : null}
66
+ {showStatus ? (
67
+ <div className={b('status')}>
68
+ {status ? <Text variant="subheader-2">{status}</Text> : null}
69
+ {issues ? <ResultIssues data={issues} /> : null}
70
+ </div>
71
+ ) : null}
72
+ {state === 'ready' ? (
73
+ <Graph explain={prepared} theme={theme} />
74
+ ) : (
75
+ <div>{i18n(PLAN_STATE_MESSAGES[state])}</div>
76
+ )}
77
+ </div>
78
+ );
79
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "description_no-plan": "The query has not reported its plan yet.",
3
+ "description_unparsed-plan": "The plan cannot be displayed.",
4
+ "description_unsupported-plan": "The plan version is not supported and cannot be rendered."
5
+ }
@@ -0,0 +1,7 @@
1
+ import {registerKeysets} from '../../../../../utils/i18n';
2
+
3
+ import en from './en.json';
4
+
5
+ const COMPONENT = 'ydb-tenant-diagnostics-streaming-query-graph';
6
+
7
+ export default registerKeysets(COMPONENT, {en});
@@ -0,0 +1 @@
1
+ export {StreamingQueryGraph} from './StreamingQueryGraph';
@@ -15,6 +15,7 @@
15
15
  "title_describe": "Describe",
16
16
  "title_hot-keys": "Hot keys",
17
17
  "title_graph": "Graph",
18
+ "title_computation-graph": "Computation Graph",
18
19
  "title_consumers": "Consumers",
19
20
  "title_partitions": "Partitions",
20
21
  "title_data": "Data",
@@ -19,6 +19,7 @@ import {HotKeys} from './HotKeys/HotKeys';
19
19
  import {NetworkWrapper} from './Network/NetworkWrapper';
20
20
  import {Partitions} from './Partitions/Partitions';
21
21
  import {StorageUsage} from './StorageUsage/StorageUsage';
22
+ import {StreamingQueryGraph} from './StreamingQueryGraph';
22
23
  import {TenantOverview} from './TenantOverview/TenantOverview';
23
24
  import {TopQueries} from './TopQueries/TopQueries';
24
25
  import {TopShards} from './TopShards/TopShards';
@@ -182,6 +183,9 @@ const diagnosticsTabRenderers: Record<TenantDiagnosticsTab, DiagnosticsTabRender
182
183
  database,
183
184
  scrollContainerRef,
184
185
  }),
186
+ [TENANT_DIAGNOSTICS_TABS_IDS.computationGraph]: ({database, path}) => (
187
+ <StreamingQueryGraph database={database} path={path} />
188
+ ),
185
189
  [TENANT_DIAGNOSTICS_TABS_IDS.monitoring]: ({database, databaseFullPath}) => {
186
190
  const content = uiFactory.renderMonitoring?.({database, databaseFullPath});
187
191
 
@@ -119,6 +119,14 @@ export function YqlEditor({
119
119
  const currentStatementCleanupRef = React.useRef<VoidFunction>();
120
120
  const [isEditorMounted, setIsEditorMounted] = React.useState(false);
121
121
 
122
+ React.useEffect(() => {
123
+ // onEditorReady removes the parent's visibility:hidden loading state.
124
+ // Focus after that render commits; focusing during editorDidMount is too early.
125
+ if (isEditorMounted) {
126
+ editorRef.current?.focus();
127
+ }
128
+ }, [isEditorMounted]);
129
+
122
130
  const isMultiTabQueryEditorEnabled = useMultiTabQueryEditorEnabled();
123
131
  const isAutocompleteEnabled = useEventHandler(() => Boolean(enableAutocomplete));
124
132
 
@@ -330,7 +338,6 @@ export function YqlEditor({
330
338
  if (!isMultiTabQueryEditorEnabled) {
331
339
  initUserPrompt(editor, getLastQueryText);
332
340
  }
333
- editor.focus();
334
341
  editor.addAction({
335
342
  id: 'sendQuery',
336
343
  label: i18n('action_send-query'),
@@ -106,6 +106,17 @@ WHERE Query NOT LIKE '%${QUERY_TECHNICAL_MARK}%'
106
106
  AND QueryStartAt is not null`;
107
107
  }
108
108
 
109
+ function getRunningQueriesByPoolText(poolName: string) {
110
+ const escapedPoolName = poolName.replace(/'/g, "''");
111
+ return `${QUERY_TECHNICAL_MARK}
112
+ SELECT
113
+ COUNT_IF(State = 'EXECUTING' AND (WmState IS NULL OR WmState NOT IN ('PENDING', 'DELAYED'))) as RunningQueriesCount,
114
+ COUNT_IF(State = 'QUEUED' OR WmState = 'PENDING' OR WmState = 'DELAYED') as QueuedQueriesCount
115
+ FROM \`.sys/query_sessions\`
116
+ WHERE Query NOT LIKE '%${QUERY_TECHNICAL_MARK}%'
117
+ AND WmPoolId = '${escapedPoolName}'`;
118
+ }
119
+
109
120
  interface QueriesRequestParams {
110
121
  database: string;
111
122
  filters?: TopQueriesFilters;
@@ -233,6 +244,41 @@ export const topQueriesApi = api.injectEndpoints({
233
244
  },
234
245
  providesTags: ['All'],
235
246
  }),
247
+ getRunningQueriesByPool: build.query({
248
+ queryFn: async (
249
+ {database, poolName}: {database: string; poolName: string},
250
+ {signal},
251
+ ) => {
252
+ try {
253
+ const response = await window.api.viewer.sendQuery(
254
+ {
255
+ query: getRunningQueriesByPoolText(poolName),
256
+ database,
257
+ action: 'execute-query',
258
+ internal_call: true,
259
+ },
260
+ {signal, withRetries: true},
261
+ );
262
+
263
+ if (isQueryErrorResponse(response)) {
264
+ throw response;
265
+ }
266
+
267
+ const data = parseQueryAPIResponse(response);
268
+ const row = data?.resultSets?.[0]?.result?.[0];
269
+
270
+ return {
271
+ data: {
272
+ runningQueriesCount: Number(row?.RunningQueriesCount) || 0,
273
+ queuedQueriesCount: Number(row?.QueuedQueriesCount) || 0,
274
+ },
275
+ };
276
+ } catch (error) {
277
+ return {error};
278
+ }
279
+ },
280
+ providesTags: ['All'],
281
+ }),
236
282
  }),
237
283
  overrideExisting: 'throw',
238
284
  });
@@ -0,0 +1,74 @@
1
+ import type {QueryPlan} from '../../../../types/api/query';
2
+ import type {PreparedQueryData} from '../types';
3
+
4
+ import {preparePlanData} from './preparePlanData';
5
+
6
+ // empty: nothing has been published yet; unparsed: a plan arrived but cannot be read,
7
+ // for instance truncated by the backend; unsupported: a readable plan of an unknown version.
8
+ export type StreamingQueryPlanState = 'empty' | 'unparsed' | 'unsupported' | 'ready';
9
+
10
+ interface StreamingQueryPlan {
11
+ state: StreamingQueryPlanState;
12
+ prepared?: PreparedQueryData['preparedPlan'];
13
+ }
14
+
15
+ function isObject(value: unknown): value is Record<string, unknown> {
16
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
17
+ }
18
+
19
+ function isPlanNode(value: unknown) {
20
+ return isObject(value) && typeof value['Node Type'] === 'string';
21
+ }
22
+
23
+ function isStringList(value: unknown) {
24
+ return (
25
+ value === undefined || (Array.isArray(value) && value.every((i) => typeof i === 'string'))
26
+ );
27
+ }
28
+
29
+ // The graph blocks render these fields directly, so anything else would throw while drawing.
30
+ function isRenderable(prepared: PreparedQueryData['preparedPlan']) {
31
+ return (prepared?.nodes ?? []).every(({data}) => {
32
+ return (
33
+ (data?.id === undefined || typeof data.id === 'number') &&
34
+ (data?.name === undefined || typeof data.name === 'string') &&
35
+ isStringList(data?.operators) &&
36
+ isStringList(data?.tables)
37
+ );
38
+ });
39
+ }
40
+
41
+ export function prepareStreamingQueryPlan(planText?: string): StreamingQueryPlan {
42
+ if (!planText) {
43
+ return {state: 'empty'};
44
+ }
45
+
46
+ let parsed: unknown;
47
+ try {
48
+ parsed = JSON.parse(planText);
49
+ } catch {
50
+ return {state: 'unparsed'};
51
+ }
52
+
53
+ if (!isObject(parsed) || !isObject(parsed.meta)) {
54
+ return {state: 'empty'};
55
+ }
56
+ if (!parsed.Plan) {
57
+ return {state: 'empty'};
58
+ }
59
+ if (!isPlanNode(parsed.Plan)) {
60
+ return {state: 'unparsed'};
61
+ }
62
+
63
+ try {
64
+ const {simplifiedPlan: _simplifiedPlan, ...prepared} = preparePlanData(
65
+ parsed as unknown as QueryPlan,
66
+ );
67
+ if (!isRenderable(prepared)) {
68
+ return {state: 'unparsed'};
69
+ }
70
+ return prepared.nodes?.length ? {state: 'ready', prepared} : {state: 'unsupported'};
71
+ } catch {
72
+ return {state: 'unparsed'};
73
+ }
74
+ }
@@ -14,27 +14,54 @@ WHERE Path = '${safePath}'
14
14
  LIMIT 1`;
15
15
  }
16
16
 
17
+ function getStreamingQueryPlanSQL(path: string) {
18
+ const safePath = path.replace(/'/g, "''");
19
+ return `${QUERY_TECHNICAL_MARK}
20
+ SELECT
21
+ Status,
22
+ Issues,
23
+ Plan
24
+ FROM \`.sys/streaming_queries\`
25
+ WHERE Path = '${safePath}'
26
+ LIMIT 1`;
27
+ }
28
+
29
+ async function execStreamingSQL(
30
+ sql: string,
31
+ {database, signal}: {database: string; signal: AbortSignal},
32
+ ) {
33
+ const response = await window.api.viewer.sendQuery(
34
+ {query: sql, database, action: 'execute-query', internal_call: true},
35
+ {signal, withRetries: true},
36
+ );
37
+ if (isQueryErrorResponse(response)) {
38
+ return {error: response};
39
+ }
40
+ return {data: parseQueryAPIResponse(response)};
41
+ }
42
+
17
43
  export const streamingQueriesApi = api.injectEndpoints({
18
44
  endpoints: (build) => ({
19
45
  getStreamingQueryInfo: build.query({
20
46
  queryFn: async ({database, path}: {database: string; path: string}, {signal}) => {
21
47
  try {
22
- const response = await window.api.viewer.sendQuery(
23
- {
24
- query: getStreamingQueryInfoSQL(path),
25
- database,
26
- action: 'execute-query',
27
- internal_call: true,
28
- },
29
- {signal, withRetries: true},
30
- );
31
-
32
- if (isQueryErrorResponse(response)) {
33
- return {error: response};
34
- }
35
-
36
- const data = parseQueryAPIResponse(response);
37
- return {data};
48
+ return await execStreamingSQL(getStreamingQueryInfoSQL(path), {
49
+ database,
50
+ signal,
51
+ });
52
+ } catch (error) {
53
+ return {error};
54
+ }
55
+ },
56
+ providesTags: ['All'],
57
+ }),
58
+ getStreamingQueryPlan: build.query({
59
+ queryFn: async ({database, path}: {database: string; path: string}, {signal}) => {
60
+ try {
61
+ return await execStreamingSQL(getStreamingQueryPlanSQL(path), {
62
+ database,
63
+ signal,
64
+ });
38
65
  } catch (error) {
39
66
  return {error};
40
67
  }
@@ -37,6 +37,7 @@ export const TENANT_DIAGNOSTICS_TABS_IDS = {
37
37
  access: 'access',
38
38
  backups: 'backups',
39
39
  monitoring: 'monitoring',
40
+ computationGraph: 'computationGraph',
40
41
  } as const;
41
42
 
42
43
  export const TENANT_SUMMARY_TABS_IDS = {
@@ -1,5 +1,5 @@
1
1
  import type {EColumnCodec, EUnit, TPathID, TStorageSettings, TTypeInfo} from './shared';
2
- import type {TFamilyDescription} from './table';
2
+ import type {TFamilyDescription, TTTLTier} from './table';
3
3
 
4
4
  export interface TColumnTableDescription {
5
5
  Name?: string;
@@ -71,6 +71,7 @@ interface TTtl {
71
71
  ExpireAfterBytes?: string;
72
72
 
73
73
  ColumnUnit?: EUnit;
74
+ Tiers?: TTTLTier[];
74
75
  }
75
76
 
76
77
  interface TColumnTableSchema {
@@ -2,43 +2,66 @@ import type {TPathID} from './shared';
2
2
 
3
3
  export interface TSysViewDescription {
4
4
  Name?: string;
5
- Type?: ESysViewType;
5
+ /**
6
+ * ESysViewType: enum name ("EVSlots") on YDB <= 26.3.1.x,
7
+ * numeric value (13) since ydb-platform/ydb#54332
8
+ */
9
+ Type?: ESysViewType | number;
6
10
  SourceObject?: TPathID;
7
11
  }
8
12
 
9
- export type ESysViewType =
10
- | 'EPartitionStats'
11
- | 'ENodes'
12
- | 'ETopQueriesByDurationOneMinute'
13
- | 'ETopQueriesByDurationOneHour'
14
- | 'ETopQueriesByReadBytesOneMinute'
15
- | 'ETopQueriesByReadBytesOneHour'
16
- | 'ETopQueriesByCpuTimeOneMinute'
17
- | 'ETopQueriesByCpuTimeOneHour'
18
- | 'ETopQueriesByRequestUnitsOneMinute'
19
- | 'ETopQueriesByRequestUnitsOneHour'
20
- | 'EQuerySessions'
21
- | 'EPDisks'
22
- | 'EVSlots'
23
- | 'EGroups'
24
- | 'EStoragePools'
25
- | 'EStorageStats'
26
- | 'ETablets'
27
- | 'EQueryMetricsOneMinute'
28
- | 'ETopPartitionsByCpuOneMinute'
29
- | 'ETopPartitionsByCpuOneHour'
30
- | 'ETopPartitionsByTliOneMinute'
31
- | 'ETopPartitionsByTliOneHour'
32
- | 'EResourcePoolClassifiers'
33
- | 'EResourcePools'
34
- | 'EAuthUsers'
35
- | 'EAuthGroups'
36
- | 'EAuthGroupMembers'
37
- | 'EAuthOwners'
38
- | 'EAuthPermissions'
39
- | 'EAuthEffectivePermissions'
40
- | 'EPgTables'
41
- | 'EInformationSchemaTables'
42
- | 'EPgClass'
43
- | 'EShowCreate'
44
- | 'ECompileCacheQueries';
13
+ /**
14
+ * Numeric ESysViewType values
15
+ * source: https://github.com/ydb-platform/ydb/blob/main/ydb/core/protos/sys_view_types.proto
16
+ */
17
+ export const SYS_VIEW_TYPE_IDS = {
18
+ EPartitionStats: 1,
19
+ ENodes: 2,
20
+ ETopQueriesByDurationOneMinute: 3,
21
+ ETopQueriesByDurationOneHour: 4,
22
+ ETopQueriesByReadBytesOneMinute: 5,
23
+ ETopQueriesByReadBytesOneHour: 6,
24
+ ETopQueriesByCpuTimeOneMinute: 7,
25
+ ETopQueriesByCpuTimeOneHour: 8,
26
+ ETopQueriesByRequestUnitsOneMinute: 9,
27
+ ETopQueriesByRequestUnitsOneHour: 10,
28
+ EQuerySessions: 11,
29
+ EPDisks: 12,
30
+ EVSlots: 13,
31
+ EGroups: 14,
32
+ EStoragePools: 15,
33
+ EStorageStats: 16,
34
+ ETablets: 17,
35
+ EQueryMetricsOneMinute: 18,
36
+ ETopPartitionsByCpuOneMinute: 19,
37
+ ETopPartitionsByCpuOneHour: 20,
38
+ ETopPartitionsByTliOneMinute: 21,
39
+ ETopPartitionsByTliOneHour: 22,
40
+ EResourcePoolClassifiers: 23,
41
+ EResourcePools: 24,
42
+ EAuthUsers: 25,
43
+ EAuthGroups: 26,
44
+ EAuthGroupMembers: 27,
45
+ EAuthOwners: 28,
46
+ EAuthPermissions: 29,
47
+ EAuthEffectivePermissions: 30,
48
+ EPgTables: 31,
49
+ EInformationSchemaTables: 32,
50
+ EPgClass: 33,
51
+ EShowCreate: 34,
52
+ ECompileCacheQueries: 35,
53
+ EStorePrimaryIndexStats: 36,
54
+ EStorePrimaryIndexSchemaStats: 37,
55
+ EStorePrimaryIndexPortionStats: 38,
56
+ EStorePrimaryIndexGranuleStats: 39,
57
+ EStorePrimaryIndexOptimizerStats: 40,
58
+ ETablePrimaryIndexStats: 41,
59
+ ETablePrimaryIndexSchemaStats: 42,
60
+ ETablePrimaryIndexPortionStats: 43,
61
+ ETablePrimaryIndexGranuleStats: 44,
62
+ ETablePrimaryIndexOptimizerStats: 45,
63
+ EStreamingQueries: 46,
64
+ EUdfModules: 47,
65
+ } as const;
66
+
67
+ export type ESysViewType = keyof typeof SYS_VIEW_TYPE_IDS;
@@ -66,6 +66,13 @@ interface TEnabled {
66
66
  ExpireAfterSeconds?: number;
67
67
  ColumnUnit?: EUnit;
68
68
  SysSettings?: TSysSettings;
69
+ Tiers?: TTTLTier[];
70
+ }
71
+
72
+ export interface TTTLTier {
73
+ ApplyAfterSeconds?: number;
74
+ Delete?: {};
75
+ EvictToExternalStorage?: {Storage?: string};
69
76
  }
70
77
 
71
78
  interface TSysSettings {
@@ -253,6 +253,61 @@ export function isErrorResponse(data: unknown): data is ErrorResponse {
253
253
  return Boolean(data && typeof data === 'object' && 'issues' in data);
254
254
  }
255
255
 
256
+ function hasIssueContent(data: ErrorResponse) {
257
+ return Boolean(data.error || data.issues?.length);
258
+ }
259
+
260
+ // The issues view walks this tree and renders these fields, so anything else would throw while drawing.
261
+ function isIssueTree(value: unknown): boolean {
262
+ return (
263
+ Array.isArray(value) &&
264
+ value.every((issue) => {
265
+ if (!issue || typeof issue !== 'object' || Array.isArray(issue)) {
266
+ return false;
267
+ }
268
+ const {
269
+ message,
270
+ issue_code: issueCode,
271
+ issues,
272
+ } = issue as {
273
+ message?: unknown;
274
+ issue_code?: unknown;
275
+ issues?: unknown;
276
+ };
277
+ return (
278
+ (message === undefined || typeof message === 'string') &&
279
+ (issueCode === undefined || typeof issueCode === 'number') &&
280
+ (issues === undefined || issues === null || isIssueTree(issues))
281
+ );
282
+ })
283
+ );
284
+ }
285
+
286
+ function hasRenderableIssues(data: ErrorResponse) {
287
+ const message: unknown = data.error?.message;
288
+ return (
289
+ (message === undefined || typeof message === 'string') &&
290
+ (data.issues === undefined || data.issues === null || isIssueTree(data.issues))
291
+ );
292
+ }
293
+
294
+ export function parseIssuesData(raw: unknown): ErrorResponse | string | undefined {
295
+ if (typeof raw === 'string' && raw) {
296
+ try {
297
+ const parsed: unknown = JSON.parse(raw);
298
+ return isErrorResponse(parsed) && hasIssueContent(parsed) && hasRenderableIssues(parsed)
299
+ ? parsed
300
+ : undefined;
301
+ } catch {
302
+ return raw;
303
+ }
304
+ }
305
+ if (isErrorResponse(raw) && hasIssueContent(raw) && hasRenderableIssues(raw)) {
306
+ return raw;
307
+ }
308
+ return undefined;
309
+ }
310
+
256
311
  // Although schema is set in request, if schema is not supported default schema for the version will be used
257
312
  // So we should additionally parse response
258
313
  export function parseQueryAPIResponse(
@@ -1,14 +1,31 @@
1
1
  import type {ESysViewType} from '../types/api/schema/sysView';
2
+ import {SYS_VIEW_TYPE_IDS} from '../types/api/schema/sysView';
2
3
 
3
- export function prepareSystemViewType(type?: ESysViewType) {
4
- if (!type) {
4
+ const SYS_VIEW_TYPE_NAMES_BY_ID = new Map<number, ESysViewType>(
5
+ (Object.keys(SYS_VIEW_TYPE_IDS) as ESysViewType[]).map(
6
+ (name) => [SYS_VIEW_TYPE_IDS[name], name] as const,
7
+ ),
8
+ );
9
+
10
+ /**
11
+ * Normalizes SysViewDescription.Type for display.
12
+ * YDB <= 26.3.1.x returns the enum name ("EVSlots"),
13
+ * YDB with ydb-platform/ydb#54332 returns the numeric value (13).
14
+ */
15
+ export function prepareSystemViewType(type?: string | number | null): string | undefined {
16
+ if (type === undefined || type === null || type === '') {
5
17
  return undefined;
6
18
  }
19
+
20
+ // Ids unknown to this UI version are shown as-is instead of hiding the row
21
+ const name =
22
+ typeof type === 'number' ? (SYS_VIEW_TYPE_NAMES_BY_ID.get(type) ?? String(type)) : type;
23
+
7
24
  // System view type is enum, its format from backend is EType
8
25
  // We need to display only Type, remove redundant E
9
26
  // EStoragePools -> StoragePools, EStorageStats -> StorageStats
10
- if (type.startsWith('E')) {
11
- return type.slice(1);
27
+ if (name.startsWith('E')) {
28
+ return name.slice(1);
12
29
  }
13
- return type;
30
+ return name;
14
31
  }