ydb-embedded-ui 6.10.1 → 6.10.3
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/README.md +7 -10
- package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +2 -3
- package/dist/containers/Tenant/Diagnostics/Overview/Overview.js +17 -10
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/TableInfo.js +2 -2
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo.d.ts +1 -1
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo.js +3 -1
- package/dist/containers/Tenant/ObjectSummary/ObjectSummary.js +4 -4
- package/dist/containers/Tenant/Query/ExplainResult/ExplainResult.scss +0 -4
- package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.js +15 -5
- package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.js +2 -5
- package/dist/containers/Tenant/Tenant.js +2 -2
- package/dist/store/reducers/schema/schema.d.ts +13 -2
- package/dist/store/reducers/schema/schema.js +36 -21
- package/dist/styles/mixins.scss +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
@@ -12,8 +12,8 @@ You can preview working UI using YDB docker image. It will be UI with the latest
|
|
12
12
|
Run on a machine with Docker installed:
|
13
13
|
|
14
14
|
```
|
15
|
-
docker pull
|
16
|
-
docker run -dp 8765:8765
|
15
|
+
docker pull ghcr.io/ydb-platform/local-ydb:nightly
|
16
|
+
docker run -dp 8765:8765 ghcr.io/ydb-platform/local-ydb:nightly
|
17
17
|
```
|
18
18
|
|
19
19
|
Open http://localhost:8765 to view it in the browser.
|
@@ -25,7 +25,7 @@ Open http://localhost:8765 to view it in the browser.
|
|
25
25
|
docker run --rm -ti --name ydb-local -h localhost \
|
26
26
|
-p 8765:8765 \
|
27
27
|
-e MON_PORT=8765 \
|
28
|
-
|
28
|
+
ghcr.io/ydb-platform/local-ydb:nightly
|
29
29
|
```
|
30
30
|
2. Install dependencies with `npm ci`
|
31
31
|
3. Run the frontend app in the development mode, via invoking `npm run dev`
|
@@ -38,11 +38,8 @@ For API reference, open Swagger UI on http://localhost:8765/viewer/api/.
|
|
38
38
|
|
39
39
|
[Docs on YDB docker images](https://ydb.tech/en/docs/getting_started/self_hosted/ydb_docker)
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
To test new features, you can use ydb version that is currently in testing mode with `cr.yandex/yc/yandex-docker-local-ydb:edge` image
|
44
|
-
or use a build from `main` brunch with `ghcr.io/ydb-platform/local-ydb:nightly` image.
|
45
|
-
Also you can set specific version like `cr.yandex/yc/yandex-docker-local-ydb:23.1`
|
41
|
+
To test new features, you can use ydb version built from `main` brunch with `ghcr.io/ydb-platform/local-ydb:nightly` image.
|
42
|
+
Also you can set specific version like `ghcr.io/ydb-platform/local-ydb:24.1`
|
46
43
|
|
47
44
|
### Custom configuration in dev mode with .env file
|
48
45
|
|
@@ -105,7 +102,7 @@ npm run test:e2e
|
|
105
102
|
|
106
103
|
### CI
|
107
104
|
|
108
|
-
E2E tests are run in CI in `e2e_tests` job. Tests run on Playwright `webServer` (it is started with `npm run dev`), `webServer` uses docker container `
|
105
|
+
E2E tests are run in CI in `e2e_tests` job. Tests run on Playwright `webServer` (it is started with `npm run dev`), `webServer` uses docker container `ghcr.io/ydb-platform/local-ydb:nightly` as backend.
|
109
106
|
|
110
107
|
## Making a production bundle.
|
111
108
|
|
@@ -118,7 +115,7 @@ To test production bundle with latest YDB backend release, do the following:
|
|
118
115
|
|
119
116
|
1. Install dependencies with `npm ci`
|
120
117
|
2. Build a production bundle with a few tweaks for embedded version: `npm run build:embedded`.
|
121
|
-
3. Invoke `docker run -it --hostname localhost -dp 2135:2135 -p 8765:8765 -v ~/projects/ydb-embedded-ui/build:/ydb_data/node_1/content/monitoring
|
118
|
+
3. Invoke `docker run -it --hostname localhost -dp 2135:2135 -p 8765:8765 -v ~/projects/ydb-embedded-ui/build:/ydb_data/node_1/content/monitoring ghcr.io/ydb-platform/local-ydb:nightly`
|
122
119
|
4. Open [embedded YDB UI](http://localhost:8765/monitoring) to view it in the browser.
|
123
120
|
|
124
121
|
### Testing production bundle with specific cluster host
|
@@ -6,7 +6,7 @@ import { Button, Card, Icon } from '@gravity-ui/uikit';
|
|
6
6
|
import { ResponseError } from '../../../../components/Errors/ResponseError';
|
7
7
|
import { ResizeableDataTable } from '../../../../components/ResizeableDataTable/ResizeableDataTable';
|
8
8
|
import { hotKeysApi } from '../../../../store/reducers/hotKeys/hotKeys';
|
9
|
-
import {
|
9
|
+
import { useGetSchemaQuery } from '../../../../store/reducers/schema/schema';
|
10
10
|
import { cn } from '../../../../utils/cn';
|
11
11
|
import { DEFAULT_TABLE_SETTINGS, IS_HOTKEYS_HELP_HIDDEN_KEY } from '../../../../utils/constants';
|
12
12
|
import { useSetting } from '../../../../utils/hooks';
|
@@ -41,8 +41,7 @@ export function HotKeys({ path }) {
|
|
41
41
|
var _a, _b;
|
42
42
|
const { currentData: data, isFetching, error } = hotKeysApi.useGetHotKeysQuery({ path });
|
43
43
|
const loading = isFetching && data === undefined;
|
44
|
-
const {
|
45
|
-
const schemaLoading = schemaIsFetching && schemaData === undefined;
|
44
|
+
const { data: schemaData, isLoading: schemaLoading } = useGetSchemaQuery({ path });
|
46
45
|
const keyColumnsIds = (_b = (_a = schemaData === null || schemaData === void 0 ? void 0 : schemaData.PathDescription) === null || _a === void 0 ? void 0 : _a.Table) === null || _b === void 0 ? void 0 : _b.KeyColumnNames;
|
47
46
|
const tableColumns = React.useMemo(() => {
|
48
47
|
return getHotKeysColumns(keyColumnsIds);
|
@@ -4,25 +4,31 @@ import { shallowEqual } from 'react-redux';
|
|
4
4
|
import { ResponseError } from '../../../../components/Errors/ResponseError';
|
5
5
|
import { TableIndexInfo } from '../../../../components/InfoViewer/schemaInfo';
|
6
6
|
import { Loader } from '../../../../components/Loader';
|
7
|
-
import { olapApi } from '../../../../store/reducers/olapStats';
|
8
7
|
import { overviewApi } from '../../../../store/reducers/overview/overview';
|
9
|
-
import {
|
8
|
+
import { selectSchemaMergedChildrenPaths, useGetSchemaQuery, } from '../../../../store/reducers/schema/schema';
|
10
9
|
import { EPathType } from '../../../../types/api/schema';
|
11
10
|
import { useAutoRefreshInterval, useTypedSelector } from '../../../../utils/hooks';
|
12
11
|
import { ExternalDataSourceInfo } from '../../Info/ExternalDataSource/ExternalDataSource';
|
13
12
|
import { ExternalTableInfo } from '../../Info/ExternalTable/ExternalTable';
|
14
13
|
import { ViewInfo } from '../../Info/View/View';
|
15
|
-
import {
|
14
|
+
import { isEntityWithMergedImplementation } from '../../utils/schema';
|
16
15
|
import { AsyncReplicationInfo } from './AsyncReplicationInfo';
|
17
16
|
import { ChangefeedInfo } from './ChangefeedInfo';
|
18
17
|
import { TableInfo } from './TableInfo';
|
19
18
|
import { TopicInfo } from './TopicInfo';
|
20
19
|
function Overview({ type, path }) {
|
21
20
|
const [autoRefreshInterval] = useAutoRefreshInterval();
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
// FIXME: The request is too heavy, stats table may have millions of items
|
22
|
+
// Disabled until fixed
|
23
|
+
// https://github.com/ydb-platform/ydb-embedded-ui/issues/907
|
24
|
+
// https://github.com/ydb-platform/ydb-embedded-ui/issues/908
|
25
|
+
// const olapParams = isTableType(type) && isColumnEntityType(type) ? {path} : skipToken;
|
26
|
+
// const {currentData: olapData, isFetching: olapIsFetching} = olapApi.useGetOlapStatsQuery(
|
27
|
+
// olapParams,
|
28
|
+
// {pollingInterval: autoRefreshInterval},
|
29
|
+
// );
|
30
|
+
// const olapStatsLoading = olapIsFetching && olapData === undefined;
|
31
|
+
// const {result: olapStats} = olapData || {result: undefined};
|
26
32
|
const isEntityWithMergedImpl = isEntityWithMergedImplementation(type);
|
27
33
|
// shallowEqual prevents rerenders when new schema data is loaded
|
28
34
|
const mergedChildrenPaths = useTypedSelector((state) => selectSchemaMergedChildrenPaths(state, path, type), shallowEqual);
|
@@ -38,8 +44,9 @@ function Overview({ type, path }) {
|
|
38
44
|
});
|
39
45
|
const overviewLoading = isFetching && currentData === undefined;
|
40
46
|
const { data: rawData, additionalData } = currentData || {};
|
41
|
-
const { error: schemaError } =
|
42
|
-
|
47
|
+
const { error: schemaError } = useGetSchemaQuery({ path });
|
48
|
+
// overviewLoading || olapStatsLoading
|
49
|
+
const entityLoading = overviewLoading;
|
43
50
|
const entityNotReady = isEntityWithMergedImpl && !mergedChildrenPaths;
|
44
51
|
const renderContent = () => {
|
45
52
|
var _a;
|
@@ -65,7 +72,7 @@ function Overview({ type, path }) {
|
|
65
72
|
[EPathType.EPathTypeView]: () => _jsx(ViewInfo, { data: data }),
|
66
73
|
[EPathType.EPathTypeReplication]: () => _jsx(AsyncReplicationInfo, { data: data }),
|
67
74
|
};
|
68
|
-
return ((type && ((_a = pathTypeToComponent[type]) === null || _a === void 0 ? void 0 : _a.call(pathTypeToComponent))) || (_jsx(TableInfo, { data: data, type: type
|
75
|
+
return ((type && ((_a = pathTypeToComponent[type]) === null || _a === void 0 ? void 0 : _a.call(pathTypeToComponent))) || (_jsx(TableInfo, { data: data, type: type })));
|
69
76
|
};
|
70
77
|
if (entityLoading || entityNotReady) {
|
71
78
|
return _jsx(Loader, { size: "m" });
|
@@ -9,6 +9,6 @@ import './TableInfo.scss';
|
|
9
9
|
const b = cn('ydb-diagnostics-table-info');
|
10
10
|
export const TableInfo = ({ data, type, olapStats }) => {
|
11
11
|
const title = _jsx(EntityTitle, { data: data === null || data === void 0 ? void 0 : data.PathDescription });
|
12
|
-
const { generalInfo
|
13
|
-
return (_jsxs("div", { className: b(), children: [_jsx(InfoViewer, { info: generalInfo, title: title, className: b('info-block'), renderEmptyState: () => _jsx("div", { className: b('title'), children: title }) }), _jsxs("div", { className: b('row'), children: [_jsx("div", { className: b('col'), children: tableStatsInfo.map((info, index) => (_jsx(InfoViewer, { info: info, title: index === 0 ? i18n('tableStats') : undefined, className: b('info-block'), renderEmptyState: () => null }, index))) }), tabletMetricsInfo.length > 0 || partitionConfigInfo.length > 0 ? (_jsxs("div", { className: b('col'), children: [_jsx(InfoViewer, { info: tabletMetricsInfo, title: i18n('tabletMetrics'), className: b('info-block'), renderEmptyState: () => null }), _jsx(InfoViewer, { info: partitionConfigInfo, title: i18n('partitionConfig'), className: b('info-block'), renderEmptyState: () => null })] })) : null] })] }));
|
12
|
+
const { generalInfo, tableStatsInfo, tabletMetricsInfo = [], partitionConfigInfo = [], } = React.useMemo(() => prepareTableInfo(data, type, olapStats), [data, type, olapStats]);
|
13
|
+
return (_jsxs("div", { className: b(), children: [_jsx(InfoViewer, { info: generalInfo, title: title, className: b('info-block'), renderEmptyState: () => _jsx("div", { className: b('title'), children: title }) }), _jsxs("div", { className: b('row'), children: [tableStatsInfo ? (_jsx("div", { className: b('col'), children: tableStatsInfo.map((info, index) => (_jsx(InfoViewer, { info: info, title: index === 0 ? i18n('tableStats') : undefined, className: b('info-block'), renderEmptyState: () => null }, index))) })) : null, tabletMetricsInfo.length > 0 || partitionConfigInfo.length > 0 ? (_jsxs("div", { className: b('col'), children: [_jsx(InfoViewer, { info: tabletMetricsInfo, title: i18n('tabletMetrics'), className: b('info-block'), renderEmptyState: () => null }), _jsx(InfoViewer, { info: partitionConfigInfo, title: i18n('partitionConfig'), className: b('info-block'), renderEmptyState: () => null })] })) : null] })] }));
|
14
14
|
};
|
@@ -10,7 +10,7 @@ export declare const prepareTableInfo: (data?: TEvDescribeSchemeResult, type?: E
|
|
10
10
|
partitionConfigInfo?: undefined;
|
11
11
|
} | {
|
12
12
|
generalInfo: InfoViewerItem[];
|
13
|
-
tableStatsInfo: InfoViewerItem[][];
|
13
|
+
tableStatsInfo: InfoViewerItem[][] | undefined;
|
14
14
|
tabletMetricsInfo: InfoViewerItem[];
|
15
15
|
partitionConfigInfo: InfoViewerItem[];
|
16
16
|
};
|
@@ -120,7 +120,9 @@ export const prepareTableInfo = (data, type, olapStats) => {
|
|
120
120
|
// There is no TableStats and TabletMetrics for ColumnTables inside ColumnStore
|
121
121
|
// Therefore we parse olapStats
|
122
122
|
if (type === EPathType.EPathTypeColumnTable && isInStoreColumnTable(ColumnTableDescription)) {
|
123
|
-
|
123
|
+
if (olapStats) {
|
124
|
+
tableStatsInfo = [prepareOlapStats(olapStats)];
|
125
|
+
}
|
124
126
|
}
|
125
127
|
else {
|
126
128
|
tableStatsInfo = [
|
@@ -13,7 +13,7 @@ import { LinkWithIcon } from '../../../components/LinkWithIcon/LinkWithIcon';
|
|
13
13
|
import { Loader } from '../../../components/Loader';
|
14
14
|
import SplitPane from '../../../components/SplitPane';
|
15
15
|
import routes, { createExternalUILink, createHref } from '../../../routes';
|
16
|
-
import {
|
16
|
+
import { setShowPreview, useGetSchemaQuery } from '../../../store/reducers/schema/schema';
|
17
17
|
import { TENANT_PAGES_IDS, TENANT_QUERY_TABS_ID, TENANT_SUMMARY_TABS_IDS, } from '../../../store/reducers/tenant/constants';
|
18
18
|
import { setQueryTab, setSummaryTab, setTenantPage } from '../../../store/reducers/tenant/tenant';
|
19
19
|
import { EPathSubType, EPathType } from '../../../types/api/schema';
|
@@ -48,7 +48,7 @@ export function ObjectSummary({ type, subType, tenantName, path, onCollapseSumma
|
|
48
48
|
const queryParams = qs.parse(location.search, {
|
49
49
|
ignoreQueryPrefix: true,
|
50
50
|
});
|
51
|
-
const {
|
51
|
+
const { data: currentObjectData } = useGetSchemaQuery({ path });
|
52
52
|
const currentSchemaData = (_a = currentObjectData === null || currentObjectData === void 0 ? void 0 : currentObjectData.PathDescription) === null || _a === void 0 ? void 0 : _a.Self;
|
53
53
|
React.useEffect(() => {
|
54
54
|
const isTable = isTableType(type);
|
@@ -255,12 +255,12 @@ export function ObjectSummary({ type, subType, tenantName, path, onCollapseSumma
|
|
255
255
|
}
|
256
256
|
function ObjectTree({ tenantName, path }) {
|
257
257
|
var _a;
|
258
|
-
const {
|
258
|
+
const { data: tenantData = {}, isLoading } = useGetSchemaQuery({
|
259
259
|
path: tenantName,
|
260
260
|
});
|
261
261
|
const pathData = (_a = tenantData === null || tenantData === void 0 ? void 0 : tenantData.PathDescription) === null || _a === void 0 ? void 0 : _a.Self;
|
262
262
|
const [, setCurrentPath] = useQueryParam('schema', StringParam);
|
263
|
-
if (!pathData &&
|
263
|
+
if (!pathData && isLoading) {
|
264
264
|
// If Loader isn't wrapped with div, SplitPane doesn't calculate panes height correctly
|
265
265
|
return (_jsx("div", { children: _jsx(Loader, {}) }));
|
266
266
|
}
|
@@ -47,12 +47,8 @@
|
|
47
47
|
&__inspector {
|
48
48
|
overflow-y: auto;
|
49
49
|
|
50
|
-
width: 100%;
|
51
50
|
padding: 15px 20px;
|
52
51
|
@include json-tree-styles();
|
53
|
-
& .json-inspector__leaf.json-inspector__leaf_root.json-inspector__leaf_composite {
|
54
|
-
max-width: calc(100% - 50px);
|
55
|
-
}
|
56
52
|
|
57
53
|
&_fullscreen {
|
58
54
|
padding: 10px;
|
@@ -20,13 +20,23 @@ export function SchemaTree(props) {
|
|
20
20
|
const [parentPath, setParentPath] = React.useState('');
|
21
21
|
const [schemaTreeKey, setSchemaTreeKey] = React.useState('');
|
22
22
|
const fetchPath = async (path) => {
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
let schemaData;
|
24
|
+
do {
|
25
|
+
const promise = dispatch(schemaApi.endpoints.getSchema.initiate({ path }, { forceRefetch: true }));
|
26
|
+
const { data, originalArgs } = await promise;
|
27
|
+
promise.unsubscribe();
|
28
|
+
// Check if the result from the current request is received. rtk-query may skip the current request and
|
29
|
+
// return data from a parallel request, due to the same cache key.
|
30
|
+
if ((originalArgs === null || originalArgs === void 0 ? void 0 : originalArgs.path) === path) {
|
31
|
+
schemaData = data === null || data === void 0 ? void 0 : data[path];
|
32
|
+
break;
|
33
|
+
}
|
34
|
+
// eslint-disable-next-line no-constant-condition
|
35
|
+
} while (true);
|
36
|
+
if (!schemaData) {
|
27
37
|
throw new Error(`no describe data about path ${path}`);
|
28
38
|
}
|
29
|
-
const { PathDescription: { Children = [] } = {} } =
|
39
|
+
const { PathDescription: { Children = [] } = {} } = schemaData;
|
30
40
|
const childItems = Children.map((childData) => {
|
31
41
|
const { Name = '', PathType, PathSubType } = childData;
|
32
42
|
return {
|
@@ -4,7 +4,7 @@ import DataTable from '@gravity-ui/react-data-table';
|
|
4
4
|
import { skipToken } from '@reduxjs/toolkit/query';
|
5
5
|
import { ResizeableDataTable } from '../../../../components/ResizeableDataTable/ResizeableDataTable';
|
6
6
|
import { TableSkeleton } from '../../../../components/TableSkeleton/TableSkeleton';
|
7
|
-
import {
|
7
|
+
import { useGetSchemaQuery } from '../../../../store/reducers/schema/schema';
|
8
8
|
import { viewSchemaApi } from '../../../../store/reducers/viewSchema/viewSchema';
|
9
9
|
import { DEFAULT_TABLE_SETTINGS } from '../../../../utils/constants';
|
10
10
|
import { isColumnEntityType, isExternalTableType, isRowTableType, isViewType, } from '../../utils/schema';
|
@@ -13,10 +13,7 @@ import { prepareSchemaData, prepareViewSchema } from './prepareData';
|
|
13
13
|
import { b } from './shared';
|
14
14
|
import './SchemaViewer.scss';
|
15
15
|
export const SchemaViewer = ({ type, path, tenantName, extended = false }) => {
|
16
|
-
const {
|
17
|
-
path,
|
18
|
-
});
|
19
|
-
const loading = isFetching && schemaData === undefined;
|
16
|
+
const { data: schemaData, isLoading: loading } = useGetSchemaQuery({ path });
|
20
17
|
const viewSchemaRequestParams = isViewType(type) ? { path, database: tenantName } : skipToken;
|
21
18
|
const { data: viewColumnsData, isLoading: isViewSchemaLoading } = viewSchemaApi.useGetViewSchemaQuery(viewSchemaRequestParams);
|
22
19
|
const tableData = React.useMemo(() => {
|
@@ -6,7 +6,7 @@ import { AccessDenied } from '../../components/Errors/403';
|
|
6
6
|
import { Loader } from '../../components/Loader';
|
7
7
|
import SplitPane from '../../components/SplitPane';
|
8
8
|
import { setHeaderBreadcrumbs } from '../../store/reducers/header/header';
|
9
|
-
import {
|
9
|
+
import { useGetSchemaQuery } from '../../store/reducers/schema/schema';
|
10
10
|
import { cn } from '../../utils/cn';
|
11
11
|
import { DEFAULT_IS_TENANT_SUMMARY_COLLAPSED, DEFAULT_SIZE_TENANT_KEY } from '../../utils/constants';
|
12
12
|
import { useTypedDispatch } from '../../utils/hooks';
|
@@ -47,7 +47,7 @@ export function Tenant(props) {
|
|
47
47
|
dispatch(setHeaderBreadcrumbs('tenant', { tenantName }));
|
48
48
|
}, [tenantName, dispatch]);
|
49
49
|
const path = schema !== null && schema !== void 0 ? schema : tenantName;
|
50
|
-
const {
|
50
|
+
const { data: currentItem, error, isLoading } = useGetSchemaQuery({ path });
|
51
51
|
const { PathType: currentPathType, PathSubType: currentPathSubType } = ((_a = currentItem === null || currentItem === void 0 ? void 0 : currentItem.PathDescription) === null || _a === void 0 ? void 0 : _a.Self) || {};
|
52
52
|
let showBlockingError = false;
|
53
53
|
if (error && typeof error === 'object' && 'status' in error) {
|
@@ -21,11 +21,22 @@ export declare const schemaApi: import("@reduxjs/toolkit/query").Api<import("@re
|
|
21
21
|
}, import("@reduxjs/toolkit/query").BaseQueryFn<void, typeof import("../api")._NEVER, unknown, {}>, "All", unknown, "api">;
|
22
22
|
getSchema: import("@reduxjs/toolkit/query").QueryDefinition<{
|
23
23
|
path: string;
|
24
|
-
}, import("@reduxjs/toolkit/query").BaseQueryFn<void, typeof import("../api")._NEVER, unknown, {}>, "All",
|
25
|
-
|
24
|
+
}, import("@reduxjs/toolkit/query").BaseQueryFn<void, typeof import("../api")._NEVER, unknown, {}>, "All", {
|
25
|
+
[path: string]: TEvDescribeSchemeResult & {
|
26
|
+
partial?: boolean;
|
27
|
+
};
|
26
28
|
}, "api">;
|
27
29
|
}, "api", "All", typeof import("@reduxjs/toolkit/query").coreModuleName | typeof import("@reduxjs/toolkit/query/react").reactHooksModuleName>;
|
28
30
|
export declare const selectSchemaMergedChildrenPaths: Selector<RootState, string[] | undefined, [
|
29
31
|
string | undefined,
|
30
32
|
EPathType | undefined
|
31
33
|
]>;
|
34
|
+
export declare function useGetSchemaQuery({ path }: {
|
35
|
+
path: string;
|
36
|
+
}): {
|
37
|
+
data: (TEvDescribeSchemeResult & {
|
38
|
+
partial?: boolean;
|
39
|
+
}) | undefined;
|
40
|
+
isLoading: boolean;
|
41
|
+
error: unknown;
|
42
|
+
};
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import React from 'react';
|
1
2
|
import { createSelector } from '@reduxjs/toolkit';
|
2
3
|
import { isEntityWithMergedImplementation } from '../../../containers/Tenant/utils/schema';
|
3
4
|
import { api } from '../api';
|
@@ -44,42 +45,44 @@ export const schemaApi = api.injectEndpoints({
|
|
44
45
|
queryFn: async ({ path }, { signal }) => {
|
45
46
|
try {
|
46
47
|
const data = await window.api.getSchema({ path }, { signal });
|
47
|
-
return { data: data
|
48
|
+
return { data: data ? { [path]: data, ...getSchemaChildren(data) } : {} };
|
48
49
|
}
|
49
50
|
catch (error) {
|
50
51
|
return { error };
|
51
52
|
}
|
52
53
|
},
|
53
54
|
keepUnusedDataFor: Infinity,
|
54
|
-
|
55
|
-
const
|
56
|
-
|
57
|
-
return true;
|
58
|
-
}
|
59
|
-
return false;
|
55
|
+
serializeQueryArgs: ({ queryArgs: { path } }) => {
|
56
|
+
const parts = path.split('/');
|
57
|
+
return { path: parts[0] || parts[1] };
|
60
58
|
},
|
61
|
-
|
62
|
-
const { data } =
|
59
|
+
merge: (existing, incoming, { arg: { path } }) => {
|
60
|
+
const { [path]: data, ...children } = incoming;
|
63
61
|
if (data) {
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
const cachedData = schemaApi.endpoints.getSchema.select({ path: childPath })(state).data;
|
70
|
-
if (!cachedData) {
|
71
|
-
// not full data, but it contains PathType, which ensures seamless switch between nodes
|
72
|
-
dispatch(schemaApi.util.upsertQueryData('getSchema', { path: childPath }, { PathDescription: { Self: child }, partial: true }));
|
73
|
-
}
|
74
|
-
}
|
62
|
+
return {
|
63
|
+
...children,
|
64
|
+
...existing,
|
65
|
+
[path]: data,
|
66
|
+
};
|
75
67
|
}
|
68
|
+
return existing;
|
76
69
|
},
|
77
70
|
}),
|
78
71
|
}),
|
79
72
|
overrideExisting: 'throw',
|
80
73
|
});
|
74
|
+
function getSchemaChildren(data) {
|
75
|
+
const children = {};
|
76
|
+
const { PathDescription: { Children = [] } = {}, Path: path } = data;
|
77
|
+
for (const child of Children) {
|
78
|
+
const { Name = '' } = child;
|
79
|
+
const childPath = `${path}/${Name}`;
|
80
|
+
children[childPath] = { PathDescription: { Self: child }, Path: childPath, partial: true };
|
81
|
+
}
|
82
|
+
return children;
|
83
|
+
}
|
81
84
|
const getSchemaSelector = createSelector((path) => path, (path) => schemaApi.endpoints.getSchema.select({ path }));
|
82
|
-
const selectGetSchema = createSelector((state) => state, (_state, path) => getSchemaSelector(path), (state, selectTabletsInfo) => selectTabletsInfo(state).data);
|
85
|
+
const selectGetSchema = createSelector((state) => state, (_state, path) => path, (_state, path) => getSchemaSelector(path), (state, path, selectTabletsInfo) => { var _a; return (_a = selectTabletsInfo(state).data) === null || _a === void 0 ? void 0 : _a[path]; });
|
83
86
|
const selectSchemaChildren = (state, path) => { var _a, _b; return (_b = (_a = selectGetSchema(state, path)) === null || _a === void 0 ? void 0 : _a.PathDescription) === null || _b === void 0 ? void 0 : _b.Children; };
|
84
87
|
export const selectSchemaMergedChildrenPaths = createSelector([
|
85
88
|
(_, path) => path,
|
@@ -90,3 +93,15 @@ export const selectSchemaMergedChildrenPaths = createSelector([
|
|
90
93
|
? children === null || children === void 0 ? void 0 : children.map(({ Name }) => path + '/' + Name)
|
91
94
|
: undefined;
|
92
95
|
});
|
96
|
+
export function useGetSchemaQuery({ path }) {
|
97
|
+
const { currentData, isFetching, error, refetch } = schemaApi.useGetSchemaQuery({ path });
|
98
|
+
const data = currentData === null || currentData === void 0 ? void 0 : currentData[path];
|
99
|
+
const isLoading = isFetching && data === undefined;
|
100
|
+
const shouldLoad = !isLoading && ((!data && !error) || (data === null || data === void 0 ? void 0 : data.partial));
|
101
|
+
React.useEffect(() => {
|
102
|
+
if (shouldLoad) {
|
103
|
+
refetch();
|
104
|
+
}
|
105
|
+
}, [refetch, path, shouldLoad]);
|
106
|
+
return { data, isLoading, error };
|
107
|
+
}
|
package/dist/styles/mixins.scss
CHANGED
@@ -196,6 +196,10 @@
|
|
196
196
|
}
|
197
197
|
|
198
198
|
@mixin json-tree-styles {
|
199
|
+
width: 100%;
|
200
|
+
|
201
|
+
word-wrap: break-word;
|
202
|
+
|
199
203
|
// stylelint-disable
|
200
204
|
font-family: var(--g-font-family-monospace) !important;
|
201
205
|
font-size: var(--g-text-code-1-font-size) !important;
|
@@ -276,4 +280,8 @@
|
|
276
280
|
color: var(--g-color-text-primary);
|
277
281
|
}
|
278
282
|
}
|
283
|
+
|
284
|
+
& .json-inspector__leaf.json-inspector__leaf_root.json-inspector__leaf_composite {
|
285
|
+
max-width: calc(100% - 50px);
|
286
|
+
}
|
279
287
|
}
|