ydb-embedded-ui 6.10.1 → 6.10.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
}
|