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.
- package/dist/components/Graph/GravityGraph.js +9 -2
- package/dist/components/Graph/GravityGraph.js.map +1 -1
- package/dist/components/Graph/utils.d.ts +2 -1
- package/dist/components/Graph/utils.js +7 -0
- package/dist/components/Graph/utils.js.map +1 -1
- package/dist/components/PaginatedTable/rowLookup.js +30 -9
- package/dist/components/PaginatedTable/rowLookup.js.map +1 -1
- package/dist/components/TableKeyboardNavigation/TableKeyboardNavigation.js +5 -1
- package/dist/components/TableKeyboardNavigation/TableKeyboardNavigation.js.map +1 -1
- package/dist/containers/Authentication/Authentication.js +29 -30
- package/dist/containers/Authentication/Authentication.js.map +1 -1
- package/dist/containers/Authentication/utils.d.ts +2 -2
- package/dist/containers/Authentication/utils.js +1 -1
- package/dist/containers/Authentication/utils.js.map +1 -1
- package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.js +5 -1
- package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.js.map +1 -1
- package/dist/containers/Tenant/Diagnostics/Overview/Overview.js +7 -1
- package/dist/containers/Tenant/Diagnostics/Overview/Overview.js.map +1 -1
- package/dist/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/ResourcePoolInfo.scss +24 -0
- package/dist/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/ResourcePoolUsage.d.ts +8 -0
- package/dist/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/ResourcePoolUsage.js +34 -0
- package/dist/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/ResourcePoolUsage.js.map +1 -0
- package/dist/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/i18n/en.json +4 -1
- package/dist/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/i18n/index.d.ts +1 -1
- package/dist/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/index.d.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/index.js +1 -0
- package/dist/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/index.js.map +1 -1
- package/dist/containers/Tenant/Diagnostics/Overview/SchemaObjectInfo/SchemaObjectInfo.js +1 -1
- package/dist/containers/Tenant/Diagnostics/Overview/SchemaObjectInfo/SchemaObjectInfo.js.map +1 -1
- package/dist/containers/Tenant/Diagnostics/Overview/StreamingQueryInfo/StreamingQueryInfo.js +2 -17
- package/dist/containers/Tenant/Diagnostics/Overview/StreamingQueryInfo/StreamingQueryInfo.js.map +1 -1
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/en.json +4 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/index.d.ts +1 -1
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo/prepareColumnTableInfo.js +4 -1
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo/prepareColumnTableInfo.js.map +1 -1
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo/prepareRowTableInfo.js +4 -1
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo/prepareRowTableInfo.js.map +1 -1
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo/prepareTTL.js +41 -3
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo/prepareTTL.js.map +1 -1
- package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/StreamingQueryGraph.d.ts +7 -0
- package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/StreamingQueryGraph.js +38 -0
- package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/StreamingQueryGraph.js.map +1 -0
- package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/StreamingQueryGraph.scss +18 -0
- package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/i18n/en.json +5 -0
- package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/i18n/index.d.ts +2 -0
- package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/i18n/index.js +5 -0
- package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/i18n/index.js.map +1 -0
- package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/index.d.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/index.js +2 -0
- package/dist/containers/Tenant/Diagnostics/StreamingQueryGraph/index.js.map +1 -0
- package/dist/containers/Tenant/Diagnostics/i18n/en.json +1 -0
- package/dist/containers/Tenant/Diagnostics/i18n/index.d.ts +1 -1
- package/dist/containers/Tenant/Diagnostics/tabsContent.js +2 -0
- package/dist/containers/Tenant/Diagnostics/tabsContent.js.map +1 -1
- package/dist/containers/Tenant/Query/QueryEditor/YqlEditor/YqlEditor.js +8 -1
- package/dist/containers/Tenant/Query/QueryEditor/YqlEditor/YqlEditor.js.map +1 -1
- package/dist/store/reducers/executeTopQueries/executeTopQueries.d.ts +7 -0
- package/dist/store/reducers/executeTopQueries/executeTopQueries.js +38 -0
- package/dist/store/reducers/executeTopQueries/executeTopQueries.js.map +1 -1
- package/dist/store/reducers/query/parsers/prepareStreamingQueryPlan.d.ts +8 -0
- package/dist/store/reducers/query/parsers/prepareStreamingQueryPlan.js +53 -0
- package/dist/store/reducers/query/parsers/prepareStreamingQueryPlan.js.map +1 -0
- package/dist/store/reducers/streamingQuery/streamingQuery.d.ts +5 -1
- package/dist/store/reducers/streamingQuery/streamingQuery.js +35 -10
- package/dist/store/reducers/streamingQuery/streamingQuery.js.map +1 -1
- package/dist/store/reducers/tenant/constants.d.ts +1 -0
- package/dist/store/reducers/tenant/constants.js +1 -0
- package/dist/store/reducers/tenant/constants.js.map +1 -1
- package/dist/types/api/schema/columnEntity.d.ts +2 -1
- package/dist/types/api/schema/sysView.d.ts +59 -2
- package/dist/types/api/schema/sysView.js +53 -1
- package/dist/types/api/schema/sysView.js.map +1 -1
- package/dist/types/api/schema/table.d.ts +8 -0
- package/dist/types/api/schema/table.js.map +1 -1
- package/dist/utils/query.d.ts +1 -0
- package/dist/utils/query.js +40 -0
- package/dist/utils/query.js.map +1 -1
- package/dist/utils/schema.d.ts +6 -2
- package/dist/utils/schema.js +14 -4
- package/dist/utils/schema.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Graph/GravityGraph.tsx +9 -2
- package/src/components/Graph/utils.ts +9 -1
- package/src/components/PaginatedTable/rowLookup.ts +35 -10
- package/src/components/TableKeyboardNavigation/TableKeyboardNavigation.tsx +5 -1
- package/src/containers/Authentication/Authentication.tsx +29 -34
- package/src/containers/Authentication/utils.ts +3 -3
- package/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts +6 -1
- package/src/containers/Tenant/Diagnostics/Overview/Overview.tsx +17 -1
- package/src/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/ResourcePoolInfo.scss +24 -0
- package/src/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/ResourcePoolUsage.tsx +53 -0
- package/src/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/i18n/en.json +4 -1
- package/src/containers/Tenant/Diagnostics/Overview/ResourcePoolInfo/index.ts +1 -0
- package/src/containers/Tenant/Diagnostics/Overview/SchemaObjectInfo/SchemaObjectInfo.tsx +7 -1
- package/src/containers/Tenant/Diagnostics/Overview/StreamingQueryInfo/StreamingQueryInfo.tsx +2 -20
- package/src/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/en.json +4 -0
- package/src/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo/prepareColumnTableInfo.tsx +11 -1
- package/src/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo/prepareRowTableInfo.tsx +11 -1
- package/src/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo/prepareTTL.ts +45 -3
- package/src/containers/Tenant/Diagnostics/StreamingQueryGraph/StreamingQueryGraph.scss +18 -0
- package/src/containers/Tenant/Diagnostics/StreamingQueryGraph/StreamingQueryGraph.tsx +79 -0
- package/src/containers/Tenant/Diagnostics/StreamingQueryGraph/i18n/en.json +5 -0
- package/src/containers/Tenant/Diagnostics/StreamingQueryGraph/i18n/index.ts +7 -0
- package/src/containers/Tenant/Diagnostics/StreamingQueryGraph/index.ts +1 -0
- package/src/containers/Tenant/Diagnostics/i18n/en.json +1 -0
- package/src/containers/Tenant/Diagnostics/tabsContent.tsx +4 -0
- package/src/containers/Tenant/Query/QueryEditor/YqlEditor/YqlEditor.tsx +8 -1
- package/src/store/reducers/executeTopQueries/executeTopQueries.ts +46 -0
- package/src/store/reducers/query/parsers/prepareStreamingQueryPlan.ts +74 -0
- package/src/store/reducers/streamingQuery/streamingQuery.ts +43 -16
- package/src/store/reducers/tenant/constants.ts +1 -0
- package/src/types/api/schema/columnEntity.ts +2 -1
- package/src/types/api/schema/sysView.ts +60 -37
- package/src/types/api/schema/table.ts +7 -0
- package/src/utils/query.ts +55 -0
- package/src/utils/schema.ts +22 -5
|
@@ -21,7 +21,7 @@ import {NonSelectableConnection} from './NonSelectableConnection';
|
|
|
21
21
|
import {graphColorsConfig} from './colorsConfig';
|
|
22
22
|
import {runTreeLayout} from './runTreeLayout';
|
|
23
23
|
import type {Data} from './types';
|
|
24
|
-
import {parseCustomPropertyValue} from './utils';
|
|
24
|
+
import {getLayoutSignature, parseCustomPropertyValue} from './utils';
|
|
25
25
|
|
|
26
26
|
import './GravityGraph.scss';
|
|
27
27
|
|
|
@@ -81,6 +81,7 @@ export function GravityGraph<T>({data, onError, theme}: Props<T>) {
|
|
|
81
81
|
const {graph, start} = useGraph(config);
|
|
82
82
|
const cameraSizeRef = React.useRef({width: 0, height: 0});
|
|
83
83
|
const graphBlockIdsRef = React.useRef<TBlockId[]>([]);
|
|
84
|
+
const layoutSignatureRef = React.useRef<string>();
|
|
84
85
|
const fitFrameRef = React.useRef<number>();
|
|
85
86
|
const fitGraphToViewport = React.useCallback(() => {
|
|
86
87
|
const {width, height} = graph.cameraService.getCameraState();
|
|
@@ -125,11 +126,17 @@ export function GravityGraph<T>({data, onError, theme}: Props<T>) {
|
|
|
125
126
|
createWorker: createGraphLayoutWorker,
|
|
126
127
|
onResult: ({layout, edges}) => {
|
|
127
128
|
graphBlockIdsRef.current = layout.map(({id}) => id);
|
|
129
|
+
// Statistics-only updates keep the layout, so the camera stays where the user put it.
|
|
130
|
+
const signature = getLayoutSignature(layout, edges);
|
|
131
|
+
const sameLayout = layoutSignatureRef.current === signature;
|
|
132
|
+
layoutSignatureRef.current = signature;
|
|
128
133
|
graph.setEntities({
|
|
129
134
|
blocks: layout,
|
|
130
135
|
connections: edges,
|
|
131
136
|
});
|
|
132
|
-
|
|
137
|
+
if (!sameLayout) {
|
|
138
|
+
scheduleGraphFit();
|
|
139
|
+
}
|
|
133
140
|
},
|
|
134
141
|
onError,
|
|
135
142
|
});
|
|
@@ -3,7 +3,7 @@ import type {TBlock} from '@gravity-ui/graph';
|
|
|
3
3
|
import type {ExplainPlanNodeData} from '../../store/reducers/query/types';
|
|
4
4
|
|
|
5
5
|
import type {AbstractGraphColorsConfig} from './colorsConfig';
|
|
6
|
-
import type {Data, TreeConnection} from './types';
|
|
6
|
+
import type {Data, EdgeResult, TreeConnection} from './types';
|
|
7
7
|
|
|
8
8
|
const BLOCK_TOP_PADDING = 8;
|
|
9
9
|
const BLOCK_LINE_HEIGHT = 16;
|
|
@@ -110,3 +110,11 @@ export function parseCustomPropertyValue<T extends AbstractGraphColorsConfig>(
|
|
|
110
110
|
|
|
111
111
|
return parsed as T;
|
|
112
112
|
}
|
|
113
|
+
|
|
114
|
+
// Identifies the geometry the camera is aimed at: block placement and the edges between them.
|
|
115
|
+
export function getLayoutSignature(blocks: TBlock[], edges: EdgeResult[]) {
|
|
116
|
+
return JSON.stringify([
|
|
117
|
+
blocks.map(({id, x, y, width, height}) => [id, x, y, width, height]),
|
|
118
|
+
edges.map(({sourceBlockId, targetBlockId}) => [sourceBlockId, targetBlockId]),
|
|
119
|
+
]);
|
|
120
|
+
}
|
|
@@ -33,15 +33,11 @@ function hasChunkAdvanced(
|
|
|
33
33
|
return false;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
if (!previous.has(offset)) {
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
36
|
const previousFulfilledTimeStamp = previous.get(offset);
|
|
41
37
|
return (
|
|
42
|
-
previousFulfilledTimeStamp
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
previousFulfilledTimeStamp !== null &&
|
|
39
|
+
previousFulfilledTimeStamp !== undefined &&
|
|
40
|
+
fulfilledTimeStamp > previousFulfilledTimeStamp
|
|
45
41
|
);
|
|
46
42
|
}
|
|
47
43
|
|
|
@@ -49,15 +45,44 @@ export function getChunkLookupRevision(states: ChunkLookupState[], revision?: st
|
|
|
49
45
|
if (!revision) {
|
|
50
46
|
return serializeChunkLookupRevision(states);
|
|
51
47
|
}
|
|
48
|
+
if (states.length === 0) {
|
|
49
|
+
return revision;
|
|
50
|
+
}
|
|
52
51
|
|
|
53
52
|
const previous = parseChunkLookupRevision(revision);
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
if (states.every((state) => hasChunkAdvanced(state, previous))) {
|
|
54
|
+
return serializeChunkLookupRevision(states);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let changed = false;
|
|
58
|
+
const activeOffsets = new Set(states.map(({offset}) => offset));
|
|
59
|
+
for (const offset of previous.keys()) {
|
|
60
|
+
if (!activeOffsets.has(offset)) {
|
|
61
|
+
previous.delete(offset);
|
|
62
|
+
changed = true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
for (const {offset, status, fulfilledTimeStamp} of states) {
|
|
66
|
+
const previousFulfilledTimeStamp = previous.get(offset);
|
|
67
|
+
if (
|
|
68
|
+
(previousFulfilledTimeStamp === null || previousFulfilledTimeStamp === undefined) &&
|
|
69
|
+
status === QueryStatus.fulfilled &&
|
|
70
|
+
fulfilledTimeStamp !== undefined
|
|
71
|
+
) {
|
|
72
|
+
// Initial loading establishes a baseline, not a completed polling cycle.
|
|
73
|
+
previous.set(offset, fulfilledTimeStamp);
|
|
74
|
+
changed = true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return changed
|
|
78
|
+
? JSON.stringify(
|
|
79
|
+
Array.from(previous).sort(([leftOffset], [rightOffset]) => leftOffset - rightOffset),
|
|
80
|
+
)
|
|
56
81
|
: revision;
|
|
57
82
|
}
|
|
58
83
|
|
|
59
84
|
export function isChunkLookupPending(states: ChunkLookupState[], revision?: string) {
|
|
60
85
|
const previous = parseChunkLookupRevision(revision);
|
|
61
86
|
|
|
62
|
-
return states.some((state) => !hasChunkAdvanced(state, previous));
|
|
87
|
+
return states.length === 0 || states.some((state) => !hasChunkAdvanced(state, previous));
|
|
63
88
|
}
|
|
@@ -338,7 +338,11 @@ export function useTableKeyboardAdapter(options: AdapterOptions) {
|
|
|
338
338
|
? findRowIndex(current.key, current.index)
|
|
339
339
|
: current.index;
|
|
340
340
|
if (matchedIndex === undefined && isRowLookupPending?.(current.lookupRevision)) {
|
|
341
|
-
next = {
|
|
341
|
+
next = {
|
|
342
|
+
...current,
|
|
343
|
+
lookupRevision: getRowLookupRevision?.(current.lookupRevision),
|
|
344
|
+
pending: true,
|
|
345
|
+
};
|
|
342
346
|
} else {
|
|
343
347
|
const position = matchedIndex ?? current.index;
|
|
344
348
|
const index = isValidIndex?.(position) === false ? getLast() : position;
|
|
@@ -4,7 +4,7 @@ import {Eye, EyeSlash, Xmark} from '@gravity-ui/icons';
|
|
|
4
4
|
import {ActionTooltip, Button, Link as ExternalLink, Icon, TextInput} from '@gravity-ui/uikit';
|
|
5
5
|
import {useHistory, useLocation, useRouteMatch} from 'react-router-dom';
|
|
6
6
|
|
|
7
|
-
import routes, {getHomePagePath, parseQuery} from '../../routes';
|
|
7
|
+
import routes, {getClusterPath, getHomePagePath, parseQuery} from '../../routes';
|
|
8
8
|
import {basename} from '../../store';
|
|
9
9
|
import {authenticationApi} from '../../store/reducers/authentication/authentication';
|
|
10
10
|
import {useLoginWithDatabase, useOidcAvailable} from '../../store/reducers/capabilities/hooks';
|
|
@@ -12,11 +12,12 @@ import {cn} from '../../utils/cn';
|
|
|
12
12
|
import {BRAND_BUTTON_CLASS} from '../../utils/constants';
|
|
13
13
|
import {prepareCommonErrorMessage} from '../../utils/errors';
|
|
14
14
|
import {useMetaAuth} from '../../utils/hooks/useMetaAuth';
|
|
15
|
+
import {useTypedSelector} from '../../utils/hooks/useTypedSelector';
|
|
15
16
|
|
|
16
17
|
import i18n from './i18n';
|
|
17
18
|
import {
|
|
18
19
|
createSsoAuthorizeUrl,
|
|
19
|
-
|
|
20
|
+
getAuthReturnTo,
|
|
20
21
|
isDatabaseError,
|
|
21
22
|
isPasswordError,
|
|
22
23
|
isUserError,
|
|
@@ -36,6 +37,7 @@ function Authentication({closable = false}: AuthenticationProps) {
|
|
|
36
37
|
const history = useHistory();
|
|
37
38
|
const location = useLocation();
|
|
38
39
|
const isDirectAuthPage = Boolean(useRouteMatch({path: routes.auth, exact: true}));
|
|
40
|
+
const singleClusterMode = useTypedSelector((state) => state.singleClusterMode);
|
|
39
41
|
|
|
40
42
|
const needDatabase = useLoginWithDatabase();
|
|
41
43
|
const oidcAvailable = useOidcAvailable();
|
|
@@ -45,49 +47,40 @@ function Authentication({closable = false}: AuthenticationProps) {
|
|
|
45
47
|
const {returnUrl, database: databaseFromQuery} = parseQuery(location);
|
|
46
48
|
const currentHref = window.location.href;
|
|
47
49
|
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
+
const returnTo = React.useMemo(() => {
|
|
51
|
+
const fallbackPath = singleClusterMode
|
|
52
|
+
? getClusterPath(undefined, undefined, {withBasename: true})
|
|
53
|
+
: getHomePagePath(undefined, undefined, {withBasename: true});
|
|
50
54
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
let pathname = url.pathname;
|
|
59
|
-
|
|
60
|
-
// Remove basename from pathname since history.replace expects paths relative to basename
|
|
61
|
-
if (basename && pathname.startsWith(basename)) {
|
|
62
|
-
pathname = pathname.slice(basename.length);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Ensure pathname starts with /
|
|
66
|
-
if (pathname && !pathname.startsWith('/')) {
|
|
67
|
-
pathname = '/' + pathname;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
path = pathname + url.search;
|
|
71
|
-
}
|
|
72
|
-
return path;
|
|
73
|
-
}, [returnUrl]);
|
|
55
|
+
return getAuthReturnTo({
|
|
56
|
+
currentUrl: new URL(currentHref),
|
|
57
|
+
fallbackPath,
|
|
58
|
+
isDirectAuthPage,
|
|
59
|
+
returnUrl,
|
|
60
|
+
});
|
|
61
|
+
}, [currentHref, isDirectAuthPage, returnUrl, singleClusterMode]);
|
|
74
62
|
|
|
75
|
-
const
|
|
63
|
+
const pathname = new URL(returnTo, currentHref).pathname;
|
|
64
|
+
// Route matching for meta authentication expects a pathname without the router basename.
|
|
65
|
+
const authPath =
|
|
66
|
+
basename && pathname.startsWith(`${basename}/`)
|
|
67
|
+
? pathname.slice(basename.length)
|
|
68
|
+
: pathname;
|
|
69
|
+
const useMeta = useMetaAuth(authPath);
|
|
76
70
|
|
|
77
71
|
const ssoUrl = React.useMemo(() => {
|
|
78
72
|
if (!oidcAvailable) {
|
|
79
73
|
return undefined;
|
|
80
74
|
}
|
|
81
75
|
|
|
82
|
-
const homePath = getHomePagePath(undefined, undefined, {withBasename: true});
|
|
83
76
|
const currentUrl = new URL(currentHref);
|
|
84
|
-
const
|
|
77
|
+
const ssoReturnTo = getAuthReturnTo({
|
|
85
78
|
currentUrl,
|
|
86
|
-
fallbackPath:
|
|
79
|
+
fallbackPath: getHomePagePath(undefined, undefined, {withBasename: true}),
|
|
87
80
|
isDirectAuthPage,
|
|
88
81
|
returnUrl,
|
|
89
82
|
});
|
|
90
|
-
return createSsoAuthorizeUrl(currentUrl.host,
|
|
83
|
+
return createSsoAuthorizeUrl(currentUrl.host, ssoReturnTo);
|
|
91
84
|
}, [currentHref, isDirectAuthPage, oidcAvailable, returnUrl]);
|
|
92
85
|
|
|
93
86
|
const [login, setLogin] = React.useState('');
|
|
@@ -121,8 +114,10 @@ function Authentication({closable = false}: AuthenticationProps) {
|
|
|
121
114
|
authenticate({user: login, password, database, useMeta})
|
|
122
115
|
.unwrap()
|
|
123
116
|
.then(() => {
|
|
124
|
-
if (
|
|
125
|
-
|
|
117
|
+
if (isDirectAuthPage) {
|
|
118
|
+
window.location.replace(returnTo);
|
|
119
|
+
} else {
|
|
120
|
+
window.location.reload();
|
|
126
121
|
}
|
|
127
122
|
})
|
|
128
123
|
.catch((error) => {
|
|
@@ -11,7 +11,7 @@ export function createSsoAuthorizeUrl(host: string, returnTo: string) {
|
|
|
11
11
|
return url.href;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
interface
|
|
14
|
+
interface GetAuthReturnToParams {
|
|
15
15
|
currentUrl: URL;
|
|
16
16
|
fallbackPath: string;
|
|
17
17
|
isDirectAuthPage: boolean;
|
|
@@ -28,12 +28,12 @@ function isSafeLocalReturnTo(path: string) {
|
|
|
28
28
|
);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export function
|
|
31
|
+
export function getAuthReturnTo({
|
|
32
32
|
currentUrl,
|
|
33
33
|
fallbackPath,
|
|
34
34
|
isDirectAuthPage,
|
|
35
35
|
returnUrl,
|
|
36
|
-
}:
|
|
36
|
+
}: GetAuthReturnToParams) {
|
|
37
37
|
if (!isDirectAuthPage) {
|
|
38
38
|
const path = `${currentUrl.pathname}${currentUrl.search}${currentUrl.hash}`;
|
|
39
39
|
return isSafeLocalReturnTo(path) ? path : fallbackPath;
|
|
@@ -115,6 +115,11 @@ const graph = {
|
|
|
115
115
|
title: i18n('title_graph'),
|
|
116
116
|
};
|
|
117
117
|
|
|
118
|
+
const computationGraph = {
|
|
119
|
+
id: TENANT_DIAGNOSTICS_TABS_IDS.computationGraph,
|
|
120
|
+
title: i18n('title_computation-graph'),
|
|
121
|
+
};
|
|
122
|
+
|
|
118
123
|
const consumers = {
|
|
119
124
|
id: TENANT_DIAGNOSTICS_TABS_IDS.consumers,
|
|
120
125
|
title: i18n('title_consumers'),
|
|
@@ -241,7 +246,7 @@ const EXTERNAL_TABLE_PAGES = [overview, schema, describe, access];
|
|
|
241
246
|
|
|
242
247
|
const VIEW_PAGES = [overview, schema, describe, access];
|
|
243
248
|
|
|
244
|
-
const STREAMING_QUERY_PAGES = [overview, describe, access];
|
|
249
|
+
const STREAMING_QUERY_PAGES = [overview, computationGraph, describe, access];
|
|
245
250
|
const SECRET_PAGES = [overview, describe, access];
|
|
246
251
|
|
|
247
252
|
// verbose mapping to guarantee correct tabs for new path types
|
|
@@ -6,6 +6,7 @@ import {Loader} from '../../../../components/Loader';
|
|
|
6
6
|
import {useClusterWithProxy} from '../../../../store/reducers/cluster/cluster';
|
|
7
7
|
import {overviewApi} from '../../../../store/reducers/overview/overview';
|
|
8
8
|
import {EPathType} from '../../../../types/api/schema';
|
|
9
|
+
import {cn} from '../../../../utils/cn';
|
|
9
10
|
import {useAutoRefreshInterval} from '../../../../utils/hooks';
|
|
10
11
|
import {ViewInfo} from '../../Info/View/View';
|
|
11
12
|
import {isDomain} from '../../ObjectSummary/transformPath';
|
|
@@ -16,6 +17,7 @@ import {ChangefeedInfo} from './ChangefeedInfo';
|
|
|
16
17
|
import {DatabaseInfo} from './DatabaseInfo/DatabaseInfo';
|
|
17
18
|
import {DefaultEntityInfo} from './DefaultEntityInfo';
|
|
18
19
|
import {ResourcePoolInfo} from './ResourcePoolInfo';
|
|
20
|
+
import {ResourcePoolUsage} from './ResourcePoolInfo/ResourcePoolUsage';
|
|
19
21
|
import {SchemaObjectInfoContainer} from './SchemaObjectInfo/SchemaObjectInfoContainer';
|
|
20
22
|
import {StreamingQueryInfo} from './StreamingQueryInfo';
|
|
21
23
|
import {TableInfo} from './TableInfo';
|
|
@@ -29,6 +31,8 @@ interface OverviewProps {
|
|
|
29
31
|
databaseFullPath: string;
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
const b = cn('ydb-diagnostics-resource-pool-info');
|
|
35
|
+
|
|
32
36
|
function Overview({type, path, database, databaseFullPath}: OverviewProps) {
|
|
33
37
|
const [autoRefreshInterval] = useAutoRefreshInterval();
|
|
34
38
|
const useMetaProxy = useClusterWithProxy();
|
|
@@ -56,7 +60,19 @@ function Overview({type, path, database, databaseFullPath}: OverviewProps) {
|
|
|
56
60
|
const pathTypeToComponent: Record<EPathType, (() => React.ReactNode) | undefined> = {
|
|
57
61
|
[EPathType.EPathTypeInvalid]: undefined,
|
|
58
62
|
[EPathType.EPathTypeDir]: undefined,
|
|
59
|
-
[EPathType.EPathTypeResourcePool]: () =>
|
|
63
|
+
[EPathType.EPathTypeResourcePool]: () => (
|
|
64
|
+
<div className={b('row')}>
|
|
65
|
+
<div className={b('col')}>
|
|
66
|
+
<ResourcePoolInfo data={data} />
|
|
67
|
+
</div>
|
|
68
|
+
<div className={b('col')}>
|
|
69
|
+
<ResourcePoolUsage
|
|
70
|
+
database={database}
|
|
71
|
+
poolName={data?.PathDescription?.ResourcePoolDescription?.Name || ''}
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
),
|
|
60
76
|
[EPathType.EPathTypeSecret]: undefined,
|
|
61
77
|
[EPathType.EPathTypeTable]: renderTableInfo,
|
|
62
78
|
[EPathType.EPathTypeSysView]: undefined,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
.ydb-diagnostics-resource-pool-info {
|
|
2
|
+
$column-width: 310px;
|
|
3
|
+
|
|
4
|
+
&__row {
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-wrap: wrap;
|
|
7
|
+
justify-content: flex-start;
|
|
8
|
+
align-items: flex-start;
|
|
9
|
+
gap: var(--g-spacing-9);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
&__col {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
justify-content: flex-start;
|
|
16
|
+
align-items: stretch;
|
|
17
|
+
|
|
18
|
+
width: $column-width;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&__loader {
|
|
22
|
+
margin-top: 50px;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {Loader} from '../../../../../components/Loader';
|
|
2
|
+
import {YDBDefinitionList} from '../../../../../components/YDBDefinitionList/YDBDefinitionList';
|
|
3
|
+
import {topQueriesApi} from '../../../../../store/reducers/executeTopQueries/executeTopQueries';
|
|
4
|
+
import {cn} from '../../../../../utils/cn';
|
|
5
|
+
import {EMPTY_DATA_PLACEHOLDER} from '../../../../../utils/constants';
|
|
6
|
+
import {useAutoRefreshInterval} from '../../../../../utils/hooks';
|
|
7
|
+
|
|
8
|
+
import i18n from './i18n';
|
|
9
|
+
|
|
10
|
+
import './ResourcePoolInfo.scss';
|
|
11
|
+
|
|
12
|
+
const b = cn('ydb-diagnostics-resource-pool-info');
|
|
13
|
+
|
|
14
|
+
interface ResourcePoolUsageProps {
|
|
15
|
+
database: string;
|
|
16
|
+
poolName: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Displays running/queued query counts for a resource pool */
|
|
20
|
+
export function ResourcePoolUsage({database, poolName}: ResourcePoolUsageProps) {
|
|
21
|
+
const [autoRefreshInterval] = useAutoRefreshInterval();
|
|
22
|
+
const {currentData, isFetching, error} = topQueriesApi.useGetRunningQueriesByPoolQuery(
|
|
23
|
+
{database, poolName},
|
|
24
|
+
{skip: !poolName, pollingInterval: autoRefreshInterval, refetchOnMountOrArgChange: true},
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const loading = isFetching && currentData === undefined;
|
|
28
|
+
|
|
29
|
+
if (loading) {
|
|
30
|
+
return <Loader size="s" className={b('loader')} />;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (error || !poolName) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<YDBDefinitionList
|
|
39
|
+
items={[
|
|
40
|
+
{
|
|
41
|
+
name: i18n('field_running-queries'),
|
|
42
|
+
content: currentData?.runningQueriesCount ?? EMPTY_DATA_PLACEHOLDER,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: i18n('field_queued-queries'),
|
|
46
|
+
content: currentData?.queuedQueriesCount ?? EMPTY_DATA_PLACEHOLDER,
|
|
47
|
+
},
|
|
48
|
+
]}
|
|
49
|
+
title={i18n('title_usage')}
|
|
50
|
+
responsive
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"title_pool-settings": "Pool Settings",
|
|
3
|
+
"title_usage": "Usage",
|
|
3
4
|
"group_queue": "Queue & Concurrency",
|
|
4
5
|
"group_cpu": "CPU",
|
|
5
6
|
"field_concurrent-query-limit": "Concurrent query limit",
|
|
6
7
|
"field_queue-size": "Queue size",
|
|
7
8
|
"field_database-load-cpu-threshold": "Database load CPU threshold",
|
|
8
|
-
"field_total-cpu-limit-percent-per-node": "Total CPU limit % per node"
|
|
9
|
+
"field_total-cpu-limit-percent-per-node": "Total CPU limit % per node",
|
|
10
|
+
"field_running-queries": "Running queries",
|
|
11
|
+
"field_queued-queries": "Queued queries"
|
|
9
12
|
}
|
|
@@ -42,6 +42,12 @@ export function SchemaObjectInfo({
|
|
|
42
42
|
);
|
|
43
43
|
|
|
44
44
|
return (
|
|
45
|
-
<YDBDefinitionList
|
|
45
|
+
<YDBDefinitionList
|
|
46
|
+
items={items}
|
|
47
|
+
responsive
|
|
48
|
+
className={b('list')}
|
|
49
|
+
wrapperClassName={b()}
|
|
50
|
+
dataQa="schema-object-info"
|
|
51
|
+
/>
|
|
46
52
|
);
|
|
47
53
|
}
|
package/src/containers/Tenant/Diagnostics/Overview/StreamingQueryInfo/StreamingQueryInfo.tsx
CHANGED
|
@@ -7,7 +7,6 @@ import type {YDBDefinitionListItem} from '../../../../../components/YDBDefinitio
|
|
|
7
7
|
import {YDBDefinitionList} from '../../../../../components/YDBDefinitionList/YDBDefinitionList';
|
|
8
8
|
import {YQLCodePreview} from '../../../../../components/YQLCodePreview/YQLCodePreview';
|
|
9
9
|
import {streamingQueriesApi} from '../../../../../store/reducers/streamingQuery/streamingQuery';
|
|
10
|
-
import type {ErrorResponse} from '../../../../../types/api/query';
|
|
11
10
|
import type {IQueryResult} from '../../../../../types/store/query';
|
|
12
11
|
import {cn} from '../../../../../utils/cn';
|
|
13
12
|
import {
|
|
@@ -15,7 +14,7 @@ import {
|
|
|
15
14
|
stripIndentByFirstLine,
|
|
16
15
|
trimOuterEmptyLines,
|
|
17
16
|
} from '../../../../../utils/dataFormatters/dataFormatters';
|
|
18
|
-
import {
|
|
17
|
+
import {parseIssuesData} from '../../../../../utils/query';
|
|
19
18
|
import {ResultIssuesModal} from '../../../Query/Issues/Issues';
|
|
20
19
|
|
|
21
20
|
import i18n from './i18n';
|
|
@@ -89,7 +88,7 @@ function prepareStreamingQueryItems(sysData?: IQueryResult) {
|
|
|
89
88
|
const errorRaw = sysData.resultSets?.[0]?.result?.[0]?.Error;
|
|
90
89
|
|
|
91
90
|
// We use custom error check, because error type can be non-standard
|
|
92
|
-
const errorData =
|
|
91
|
+
const errorData = parseIssuesData(errorRaw);
|
|
93
92
|
|
|
94
93
|
info.push({
|
|
95
94
|
name: i18n('field_query-state'),
|
|
@@ -105,20 +104,3 @@ function prepareStreamingQueryItems(sysData?: IQueryResult) {
|
|
|
105
104
|
|
|
106
105
|
return {items: info, queryText: normalizedQueryText};
|
|
107
106
|
}
|
|
108
|
-
|
|
109
|
-
function parseErrorData(raw: unknown): ErrorResponse | string | undefined {
|
|
110
|
-
if (typeof raw === 'string') {
|
|
111
|
-
try {
|
|
112
|
-
const parsed = JSON.parse(raw);
|
|
113
|
-
return isErrorResponse(parsed) ? parsed : undefined;
|
|
114
|
-
} catch {
|
|
115
|
-
return raw;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (isErrorResponse(raw)) {
|
|
120
|
-
return raw;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return undefined;
|
|
124
|
-
}
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
"field_compression-groups": "Compression & families",
|
|
27
27
|
|
|
28
28
|
"value_ttl-config": "column: '{{columnName}}', expire after: {{expireTime}}",
|
|
29
|
+
"value_ttl-tiered-config": "column: '{{columnName}}'\n{{tiers}}",
|
|
30
|
+
"value_ttl-evict": "evict to: '{{storageName}}', after: {{expireTime}}",
|
|
31
|
+
"value_ttl-delete": "delete after: {{expireTime}}",
|
|
32
|
+
"value_ttl-unknown-action": "unknown action, after: {{expireTime}}",
|
|
29
33
|
"value_partitioning-by-size-enabled": "Enabled, split size: {{size}}",
|
|
30
34
|
"value_enabled": "Enabled",
|
|
31
35
|
"value_disabled": "Disabled",
|
|
@@ -42,7 +42,17 @@ export function prepareColumnTableGeneralInfo(columnTable: TColumnTableDescripti
|
|
|
42
42
|
if (columnTable.TtlSettings) {
|
|
43
43
|
const ttlInfo = prepareTTL(columnTable?.TtlSettings);
|
|
44
44
|
if (ttlInfo) {
|
|
45
|
-
left.push(
|
|
45
|
+
left.push({
|
|
46
|
+
...ttlInfo,
|
|
47
|
+
content: (
|
|
48
|
+
<span
|
|
49
|
+
style={{whiteSpace: 'break-spaces', wordBreak: 'break-word'}}
|
|
50
|
+
data-qa="table-ttl"
|
|
51
|
+
>
|
|
52
|
+
{ttlInfo.content}
|
|
53
|
+
</span>
|
|
54
|
+
),
|
|
55
|
+
});
|
|
46
56
|
}
|
|
47
57
|
}
|
|
48
58
|
|
|
@@ -66,7 +66,17 @@ export function prepareRowTableGeneralInfo(
|
|
|
66
66
|
if (TTLSettings) {
|
|
67
67
|
const ttlInfo = prepareTTL(TTLSettings);
|
|
68
68
|
if (ttlInfo) {
|
|
69
|
-
left.push(
|
|
69
|
+
left.push({
|
|
70
|
+
...ttlInfo,
|
|
71
|
+
content: (
|
|
72
|
+
<span
|
|
73
|
+
style={{whiteSpace: 'break-spaces', wordBreak: 'break-word'}}
|
|
74
|
+
data-qa="table-ttl"
|
|
75
|
+
>
|
|
76
|
+
{ttlInfo.content}
|
|
77
|
+
</span>
|
|
78
|
+
),
|
|
79
|
+
});
|
|
70
80
|
}
|
|
71
81
|
}
|
|
72
82
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {TColumnDataLifeCycle, TTTLSettings} from '../../../../../../types/api/schema';
|
|
2
|
+
import {EMPTY_DATA_PLACEHOLDER} from '../../../../../../utils/constants';
|
|
2
3
|
import {formatDurationToShortTimeFormat} from '../../../../../../utils/timeParsers';
|
|
3
4
|
import i18n from '../i18n';
|
|
4
5
|
|
|
@@ -8,11 +9,52 @@ import i18n from '../i18n';
|
|
|
8
9
|
* @returns Definition list item with TTL info, or undefined if TTL is not configured
|
|
9
10
|
*/
|
|
10
11
|
export function prepareTTL(ttl: TTTLSettings | TColumnDataLifeCycle) {
|
|
12
|
+
const enabled = ttl.Enabled;
|
|
13
|
+
if (!enabled?.ColumnName) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (enabled.Tiers?.length) {
|
|
18
|
+
const tiers = enabled.Tiers.flatMap((tier) => {
|
|
19
|
+
const seconds = tier.ApplyAfterSeconds;
|
|
20
|
+
if (seconds === undefined || !Number.isFinite(seconds) || seconds < 0) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const expireTime = formatDurationToShortTimeFormat(seconds * 1000, 1);
|
|
25
|
+
if (tier.EvictToExternalStorage) {
|
|
26
|
+
return [
|
|
27
|
+
i18n('value_ttl-evict', {
|
|
28
|
+
storageName: tier.EvictToExternalStorage.Storage || EMPTY_DATA_PLACEHOLDER,
|
|
29
|
+
expireTime,
|
|
30
|
+
}),
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
if (tier.Delete) {
|
|
34
|
+
return [i18n('value_ttl-delete', {expireTime})];
|
|
35
|
+
}
|
|
36
|
+
return [i18n('value_ttl-unknown-action', {expireTime})];
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Nonempty tiers supersede the legacy expiry, even if all delays are invalid.
|
|
40
|
+
if (!tiers.length) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
name: i18n('field_ttl-for-rows'),
|
|
46
|
+
content: i18n('value_ttl-tiered-config', {
|
|
47
|
+
columnName: enabled.ColumnName,
|
|
48
|
+
tiers: tiers.join('\n'),
|
|
49
|
+
}),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
11
53
|
// ExpireAfterSeconds could be 0
|
|
12
|
-
if (
|
|
54
|
+
if (enabled.ExpireAfterSeconds !== undefined) {
|
|
13
55
|
const value = i18n('value_ttl-config', {
|
|
14
|
-
columnName:
|
|
15
|
-
expireTime: formatDurationToShortTimeFormat(
|
|
56
|
+
columnName: enabled.ColumnName,
|
|
57
|
+
expireTime: formatDurationToShortTimeFormat(enabled.ExpireAfterSeconds * 1000, 1),
|
|
16
58
|
});
|
|
17
59
|
|
|
18
60
|
return {name: i18n('field_ttl-for-rows'), content: value};
|