Skip to content

Commit 504468b

Browse files
authored
feat(Diagnostics): add Schema tab with column family info (#767)
1 parent bfbd3d0 commit 504468b

File tree

12 files changed

+267
-155
lines changed

12 files changed

+267
-155
lines changed

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ Open http://localhost:8765 to view it in the browser.
2222

2323
1. Run on a machine with Docker installed:
2424
```
25-
docker pull cr.yandex/yc/yandex-docker-local-ydb
26-
docker run --hostname localhost -e YDB_ALLOW_ORIGIN="http://localhost:3000" -dp 2135:2135 -dp 8765:8765 cr.yandex/yc/yandex-docker-local-ydb
25+
docker run --rm -ti --name ydb-local -h localhost \
26+
-p 8765:8765 \
27+
-e MON_PORT=8765 \
28+
cr.yandex/yc/yandex-docker-local-ydb:latest
2729
```
2830
2. Run the frontend app in the development mode, via invoking `npm run dev`
29-
3. Open [http://localhost:3000](http://localhost:3000) to view it in the browser. The page will reload if you make edits.\
31+
3. Open [localhost:3000](http://localhost:3000) to view it in the browser. The page will reload if you make edits.\
3032
You will also see any lint errors in the console.
3133

3234
For API reference, open Swagger UI on http://localhost:8765/viewer/api/.
@@ -37,7 +39,9 @@ For API reference, open Swagger UI on http://localhost:8765/viewer/api/.
3739

3840
Image `cr.yandex/yc/yandex-docker-local-ydb` corresponds to `:latest` tag. It's the latest stable ydb version.
3941

40-
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. Also you can set specific version like `cr.yandex/yc/yandex-docker-local-ydb:23.1`
42+
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
43+
or use a build from `main` brunch with `ghcr.io/ydb-platform/local-ydb:trunk` image.
44+
Also you can set specific version like `cr.yandex/yc/yandex-docker-local-ydb:23.1`
4145

4246
### Custom backend in dev mode
4347

src/containers/Tenant/Acl/Acl.scss

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,12 @@
55
overflow: auto;
66
flex-grow: 1;
77

8-
padding: 0 12px 16px;
98
@include query-data-table;
109

1110
&__result {
1211
align-self: flex-start;
1312
}
1413

15-
&__message-container {
16-
padding: 0 12px 16px;
17-
}
18-
1914
&__owner-container {
2015
position: sticky;
2116
z-index: 2;

src/containers/Tenant/Acl/Acl.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,11 @@ export const Acl = () => {
118118
}
119119

120120
if (error) {
121-
return <ResponseError error={error} className={b('message-container')} />;
121+
return <ResponseError error={error} />;
122122
}
123123

124124
if (!loading && !acl && !owner) {
125-
return <div className={b('message-container')}>{i18n('acl.empty')}</div>;
125+
return <React.Fragment>{i18n('acl.empty')}</React.Fragment>;
126126
}
127127

128128
return (

src/containers/Tenant/Diagnostics/Diagnostics.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {Heatmap} from '../../Heatmap';
1919
import {NodesWrapper} from '../../Nodes/NodesWrapper';
2020
import {StorageWrapper} from '../../Storage/StorageWrapper';
2121
import {Tablets} from '../../Tablets';
22+
import {SchemaViewer} from '../Schema/SchemaViewer/SchemaViewer';
2223
import {TenantTabsGroups} from '../TenantPages';
2324
import {isDatabaseEntityType} from '../utils/schema';
2425

@@ -103,6 +104,9 @@ function Diagnostics(props: DiagnosticsProps) {
103104
/>
104105
);
105106
}
107+
case TENANT_DIAGNOSTICS_TABS_IDS.schema: {
108+
return <SchemaViewer path={currentSchemaPath} type={type} withFamilies />;
109+
}
106110
case TENANT_DIAGNOSTICS_TABS_IDS.topQueries: {
107111
return <TopQueries path={tenantNameString} type={type} />;
108112
}

src/containers/Tenant/Diagnostics/DiagnosticsPages.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ const overview = {
1212
title: 'Info',
1313
};
1414

15+
const schema = {
16+
id: TENANT_DIAGNOSTICS_TABS_IDS.schema,
17+
title: 'Schema',
18+
};
19+
1520
const topQueries = {
1621
id: TENANT_DIAGNOSTICS_TABS_IDS.topQueries,
1722
title: 'Top queries',
@@ -76,8 +81,8 @@ export const DATABASE_PAGES = [
7681
describe,
7782
];
7883

79-
export const TABLE_PAGES = [overview, topShards, nodes, graph, tablets, hotKeys, describe];
80-
export const COLUMN_TABLE_PAGES = [overview, topShards, nodes, graph, tablets, describe];
84+
export const TABLE_PAGES = [overview, schema, topShards, nodes, graph, tablets, hotKeys, describe];
85+
export const COLUMN_TABLE_PAGES = [overview, schema, topShards, nodes, graph, tablets, describe];
8186

8287
export const DIR_PAGES = [overview, topShards, nodes, describe];
8388

src/containers/Tenant/ObjectSummary/ObjectSummary.tsx

Lines changed: 6 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,7 @@ import {
2323
TENANT_SUMMARY_TABS_IDS,
2424
} from '../../../store/reducers/tenant/constants';
2525
import {setQueryTab, setSummaryTab, setTenantPage} from '../../../store/reducers/tenant/tenant';
26-
import type {
27-
EPathSubType,
28-
TColumnDescription,
29-
TColumnTableDescription,
30-
} from '../../../types/api/schema';
26+
import type {EPathSubType} from '../../../types/api/schema';
3127
import {EPathType} from '../../../types/api/schema';
3228
import {cn} from '../../../utils/cn';
3329
import {
@@ -48,7 +44,7 @@ import {
4844
PaneVisibilityToggleButtons,
4945
paneVisibilityToggleReducerCreator,
5046
} from '../utils/paneVisibilityToggleHelpers';
51-
import {isColumnEntityType, isExternalTable, isIndexTable, isTableType} from '../utils/schema';
47+
import {isIndexTable, isTableType} from '../utils/schema';
5248

5349
import './ObjectSummary.scss';
5450

@@ -64,29 +60,6 @@ const getTenantCommonInfoState = () => {
6460
};
6561
};
6662

67-
function prepareOlapTableSchema(tableSchema: TColumnTableDescription = {}) {
68-
const {Name, Schema} = tableSchema;
69-
70-
if (Schema) {
71-
const {Columns, KeyColumnNames} = Schema;
72-
const KeyColumnIds = KeyColumnNames?.map((name: string) => {
73-
const column = Columns?.find((el) => el.Name === name);
74-
return column?.Id;
75-
}).filter((id): id is number => id !== undefined);
76-
77-
return {
78-
Columns,
79-
KeyColumnNames,
80-
Name,
81-
KeyColumnIds,
82-
};
83-
}
84-
85-
return {
86-
Name,
87-
};
88-
}
89-
9063
interface ObjectSummaryProps {
9164
type?: EPathType;
9265
subType?: EPathSubType;
@@ -112,7 +85,6 @@ export function ObjectSummary({
11285
data,
11386
currentSchemaPath,
11487
currentSchema: currentItem = {},
115-
loading: loadingSchema,
11688
} = useTypedSelector((state) => state.schema);
11789
const {summaryTab = TENANT_SUMMARY_TABS_IDS.overview} = useTypedSelector(
11890
(state) => state.tenant,
@@ -130,21 +102,6 @@ export function ObjectSummary({
130102
const currentObjectData = currentSchemaPath ? data[currentSchemaPath] : undefined;
131103
const currentSchemaData = currentObjectData?.PathDescription?.Self;
132104

133-
let keyColumnIds: number[] | undefined;
134-
let columns: TColumnDescription[] | undefined;
135-
136-
if (isTableType(type) && isColumnEntityType(type)) {
137-
const description = currentObjectData?.PathDescription?.ColumnTableDescription;
138-
const columnTableSchema = prepareOlapTableSchema(description);
139-
keyColumnIds = columnTableSchema.KeyColumnIds;
140-
columns = columnTableSchema.Columns;
141-
} else if (isExternalTable(type)) {
142-
columns = currentObjectData?.PathDescription?.ExternalTableDescription?.Columns;
143-
} else {
144-
keyColumnIds = currentObjectData?.PathDescription?.Table?.KeyColumnIds;
145-
columns = currentObjectData?.PathDescription?.Table?.Columns;
146-
}
147-
148105
React.useEffect(() => {
149106
const isTable = isTableType(type);
150107

@@ -218,7 +175,7 @@ export function ObjectSummary({
218175
component = <InfoViewer info={[{label: 'Create time', value: createTime}]} />;
219176
}
220177

221-
return <div className={b('overview-wrapper')}>{component}</div>;
178+
return component;
222179
};
223180

224181
const renderLoader = () => {
@@ -236,12 +193,8 @@ export function ObjectSummary({
236193
return <Acl />;
237194
}
238195
case TENANT_SUMMARY_TABS_IDS.schema: {
239-
return loadingSchema ? (
240-
renderLoader()
241-
) : (
242-
<div className={b('schema')}>
243-
<SchemaViewer keyColumnIds={keyColumnIds} columns={columns} type={type} />
244-
</div>
196+
return (
197+
<SchemaViewer className={b('schema')} type={type} path={currentSchemaPath} />
245198
);
246199
}
247200
default: {
@@ -364,7 +317,7 @@ export function ObjectSummary({
364317
</div>
365318
{renderTabs()}
366319
</div>
367-
{renderTabContent()}
320+
<div className={b('overview-wrapper')}>{renderTabContent()}</div>
368321
</div>
369322
</SplitPane>
370323
</div>

src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.scss

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
.schema-viewer {
2-
padding: 0px 12px;
32
&__key-icon {
43
display: flex;
54
align-items: center;
Lines changed: 39 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,58 @@
1-
import React from 'react';
2-
3-
import type {Column} from '@gravity-ui/react-data-table';
41
import DataTable from '@gravity-ui/react-data-table';
52

6-
import {Icon} from '../../../../components/Icon';
7-
import type {EPathType, TColumnDescription} from '../../../../types/api/schema';
3+
import {TableSkeleton} from '../../../../components/TableSkeleton/TableSkeleton';
4+
import type {EPathType} from '../../../../types/api/schema';
85
import {cn} from '../../../../utils/cn';
96
import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
10-
import {isExternalTable} from '../../utils/schema';
7+
import {useTypedSelector} from '../../../../utils/hooks';
8+
9+
import {
10+
SchemaViewerColumns,
11+
prepareColumnDescriptions,
12+
prepareFamilies,
13+
prepareSchemaTableColumns,
14+
} from './helpers';
1115

1216
import './SchemaViewer.scss';
1317

1418
const b = cn('schema-viewer');
1519

16-
const SchemaViewerColumns = {
17-
id: 'Id',
18-
name: 'Name',
19-
key: 'Key',
20-
type: 'Type',
21-
notNull: 'NotNull',
22-
};
23-
2420
interface SchemaViewerProps {
25-
keyColumnIds?: number[];
26-
columns?: TColumnDescription[];
21+
className?: string;
2722
type?: EPathType;
23+
path?: string;
24+
withFamilies?: boolean;
2825
}
2926

30-
export const SchemaViewer = ({keyColumnIds = [], columns = [], type}: SchemaViewerProps) => {
31-
// Keys should be displayd by their order in keyColumnIds (Primary Key)
32-
const keyColumnsOrderValues = React.useMemo(() => {
33-
return keyColumnIds.reduce<Record<number, number>>((result, keyColumnId, index) => {
34-
// Put columns with negative values, so they will be the first with ascending sort
35-
// Minus keyColumnIds.length for the first key, -1 for the last
36-
result[keyColumnId] = index - keyColumnIds.length;
37-
return result;
38-
}, {});
39-
}, [keyColumnIds]);
27+
export const SchemaViewer = ({className, type, path, withFamilies = false}: SchemaViewerProps) => {
28+
const {data, loading} = useTypedSelector((state) => state.schema);
29+
const currentObjectData = path ? data[path] : undefined;
4030

41-
let dataTableColumns: Column<TColumnDescription>[] = [
42-
{
43-
name: SchemaViewerColumns.id,
44-
width: 40,
45-
},
46-
{
47-
name: SchemaViewerColumns.key,
48-
width: 40,
49-
// Table should start with key columns on sort click
50-
defaultOrder: DataTable.ASCENDING,
51-
sortAccessor: (row) => {
52-
// Values in keyColumnsOrderValues are always negative, so it will be 1 for not key columns
53-
return (row.Id && keyColumnsOrderValues[row.Id]) || 1;
54-
},
55-
render: ({row}) => {
56-
return row.Id && keyColumnIds.includes(row.Id) ? (
57-
<div className={b('key-icon')}>
58-
<Icon name="key" viewBox="0 0 12 7" width={12} height={7} />
59-
</div>
60-
) : null;
61-
},
62-
},
63-
{
64-
name: SchemaViewerColumns.name,
65-
width: 100,
66-
},
67-
{
68-
name: SchemaViewerColumns.type,
69-
width: 100,
70-
},
71-
{
72-
name: SchemaViewerColumns.notNull,
73-
width: 100,
74-
// Table should start with notNull columns on sort click
75-
defaultOrder: DataTable.DESCENDING,
76-
render: ({row}) => {
77-
if (row.NotNull) {
78-
return '\u2713';
79-
}
80-
81-
return undefined;
82-
},
83-
},
84-
];
85-
86-
if (isExternalTable(type)) {
87-
// External tables don't have key columns
88-
dataTableColumns = dataTableColumns.filter(
89-
(column) => column.name !== SchemaViewerColumns.key,
90-
);
91-
}
31+
const {columns, keyColumnIds} = prepareColumnDescriptions(type, currentObjectData);
32+
const families = prepareFamilies(currentObjectData);
9233

9334
return (
94-
<div className={b()}>
95-
<DataTable
96-
theme="yandex-cloud"
97-
data={columns}
98-
columns={dataTableColumns}
99-
settings={DEFAULT_TABLE_SETTINGS}
100-
initialSortOrder={{columnId: SchemaViewerColumns.key, order: DataTable.ASCENDING}}
101-
/>
35+
<div className={b(null, className)}>
36+
{loading ? (
37+
<TableSkeleton />
38+
) : (
39+
<DataTable
40+
theme="yandex-cloud"
41+
data={columns}
42+
columns={prepareSchemaTableColumns({
43+
type,
44+
b,
45+
families,
46+
keyColumnIds,
47+
withFamilies,
48+
})}
49+
settings={DEFAULT_TABLE_SETTINGS}
50+
initialSortOrder={{
51+
columnId: SchemaViewerColumns.key,
52+
order: DataTable.ASCENDING,
53+
}}
54+
/>
55+
)}
10256
</div>
10357
);
10458
};
105-
106-
export default SchemaViewer;

0 commit comments

Comments
 (0)