Skip to content

Commit 88d9041

Browse files
feat: info and summary tabs for external objects (#493)
1 parent f823378 commit 88d9041

File tree

16 files changed

+273
-14
lines changed

16 files changed

+273
-14
lines changed

src/components/InfoViewer/formatters/common.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ import {formatDateTime} from '../../../utils';
33

44
import {createInfoFormatter} from '../utils';
55

6+
import i18n from '../i18n';
7+
68
export const formatCommonItem = createInfoFormatter<TDirEntry>({
79
values: {
810
PathType: (value) => value?.substring('EPathType'.length),
911
CreateStep: formatDateTime,
1012
},
1113
labels: {
12-
PathType: 'Type',
13-
CreateStep: 'Created',
14+
PathType: i18n('common.type'),
15+
CreateStep: i18n('common.created'),
1416
},
1517
});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"common.created": "Created",
3+
"common.type": "Type"
4+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {i18n, Lang} from '../../../utils/i18n';
2+
3+
import en from './en.json';
4+
import ru from './ru.json';
5+
6+
const COMPONENT = 'ydb-components-info-viewer';
7+
8+
i18n.registerKeyset(Lang.En, COMPONENT, en);
9+
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
10+
11+
export default i18n.keyset(COMPONENT);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"common.created": "Создано",
3+
"common.type": "Тип"
4+
}

src/containers/Tenant/Diagnostics/Overview/Overview.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ import {
2727
isPathTypeWithTopic,
2828
} from '../../utils/schema';
2929

30+
import {ExternalTableInfo} from '../../Info/ExternalTable/ExternalTable';
31+
import {ExternalDataSourceInfo} from '../../Info/ExternalDataSource/ExternalDataSource';
32+
3033
import {TopicInfo} from './TopicInfo';
3134
import {ChangefeedInfo} from './ChangefeedInfo';
3235
import {TableInfo} from './TableInfo';
@@ -124,8 +127,8 @@ function Overview({type, tenantName}: OverviewProps) {
124127
<ChangefeedInfo data={data} topic={additionalData?.[0]} />
125128
),
126129
[EPathType.EPathTypePersQueueGroup]: () => <TopicInfo data={data} />,
127-
[EPathType.EPathTypeExternalTable]: undefined,
128-
[EPathType.EPathTypeExternalDataSource]: undefined,
130+
[EPathType.EPathTypeExternalTable]: () => <ExternalTableInfo data={data} />,
131+
[EPathType.EPathTypeExternalDataSource]: () => <ExternalDataSourceInfo data={data} />,
129132
};
130133

131134
return (
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.ydb-external-data-source-info {
2+
&__location {
3+
max-width: var(--tenant-object-info-max-value-width);
4+
}
5+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import block from 'bem-cn-lite';
2+
3+
import type {TEvDescribeSchemeResult} from '../../../../types/api/schema';
4+
import {useTypedSelector} from '../../../../utils/hooks';
5+
6+
import {InfoViewer, InfoViewerItem} from '../../../../components/InfoViewer';
7+
import {formatCommonItem} from '../../../../components/InfoViewer/formatters';
8+
import EntityStatus from '../../../../components/EntityStatus/EntityStatus';
9+
import {ResponseError} from '../../../../components/Errors/ResponseError';
10+
11+
import {getEntityName} from '../../utils';
12+
13+
import i18n from '../i18n';
14+
import './ExternalDataSource.scss';
15+
16+
const b = block('ydb-external-data-source-info');
17+
18+
const prepareExternalDataSourceSummary = (data: TEvDescribeSchemeResult): InfoViewerItem[] => {
19+
return [
20+
{
21+
label: i18n('external-objects.source-type'),
22+
value: data.PathDescription?.ExternalDataSourceDescription?.SourceType,
23+
},
24+
formatCommonItem('CreateStep', data.PathDescription?.Self?.CreateStep),
25+
];
26+
};
27+
28+
const prepareExternalDataSourceInfo = (data: TEvDescribeSchemeResult): InfoViewerItem[] => {
29+
const {Location, Auth} = data.PathDescription?.ExternalDataSourceDescription || {};
30+
31+
return [
32+
...prepareExternalDataSourceSummary(data),
33+
{
34+
label: i18n('external-objects.location'),
35+
value: (
36+
<EntityStatus
37+
name={Location}
38+
showStatus={false}
39+
hasClipboardButton
40+
clipboardButtonAlwaysVisible
41+
className={b('location')}
42+
/>
43+
),
44+
},
45+
{
46+
label: i18n('external-objects.auth-method'),
47+
value: Auth?.ServiceAccount
48+
? i18n('external-objects.auth-method.service-account')
49+
: i18n('external-objects.auth-method.none'),
50+
},
51+
];
52+
};
53+
54+
interface ExternalDataSourceProps {
55+
data?: TEvDescribeSchemeResult;
56+
prepareData: (data: TEvDescribeSchemeResult) => InfoViewerItem[];
57+
}
58+
59+
const ExternalDataSource = ({data, prepareData}: ExternalDataSourceProps) => {
60+
const entityName = getEntityName(data?.PathDescription);
61+
62+
const {error: schemaError} = useTypedSelector((state) => state.schema);
63+
64+
if (schemaError) {
65+
return <ResponseError error={schemaError} />;
66+
}
67+
68+
if (!data) {
69+
return <div className="error">No {entityName} data</div>;
70+
}
71+
72+
return <InfoViewer title={entityName} info={prepareData(data)} />;
73+
};
74+
75+
export const ExternalDataSourceInfo = ({data}: {data?: TEvDescribeSchemeResult}) => {
76+
return <ExternalDataSource data={data} prepareData={prepareExternalDataSourceInfo} />;
77+
};
78+
79+
export const ExternalDataSourceSummary = ({data}: {data?: TEvDescribeSchemeResult}) => {
80+
return <ExternalDataSource data={data} prepareData={prepareExternalDataSourceSummary} />;
81+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.ydb-external-table-info {
2+
&__location {
3+
max-width: var(--tenant-object-info-max-value-width);
4+
}
5+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import {useLocation} from 'react-router';
2+
import block from 'bem-cn-lite';
3+
4+
import type {TEvDescribeSchemeResult} from '../../../../types/api/schema';
5+
import {useTypedSelector} from '../../../../utils/hooks';
6+
import {createHref, parseQuery} from '../../../../routes';
7+
import {formatCommonItem} from '../../../../components/InfoViewer/formatters';
8+
import {InfoViewer, InfoViewerItem} from '../../../../components/InfoViewer';
9+
import {ExternalLinkWithIcon} from '../../../../components/ExternalLinkWithIcon/ExternalLinkWithIcon';
10+
import EntityStatus from '../../../../components/EntityStatus/EntityStatus';
11+
import {ResponseError} from '../../../../components/Errors/ResponseError';
12+
13+
import {getEntityName} from '../../utils';
14+
15+
import i18n from '../i18n';
16+
import './ExternalTable.scss';
17+
18+
const b = block('ydb-external-table-info');
19+
20+
const prepareExternalTableSummary = (
21+
data: TEvDescribeSchemeResult,
22+
pathToDataSource: string,
23+
): InfoViewerItem[] => {
24+
const {CreateStep} = data.PathDescription?.Self || {};
25+
const {SourceType, DataSourcePath} = data.PathDescription?.ExternalTableDescription || {};
26+
27+
const dataSourceName = DataSourcePath?.split('/').pop();
28+
29+
return [
30+
{label: i18n('external-objects.source-type'), value: SourceType},
31+
formatCommonItem('CreateStep', CreateStep),
32+
{
33+
label: i18n('external-objects.data-source'),
34+
value: DataSourcePath && (
35+
<span title={DataSourcePath}>
36+
<ExternalLinkWithIcon title={dataSourceName || ''} url={pathToDataSource} />
37+
</span>
38+
),
39+
},
40+
];
41+
};
42+
43+
const prepareExternalTableInfo = (
44+
data: TEvDescribeSchemeResult,
45+
pathToDataSource: string,
46+
): InfoViewerItem[] => {
47+
const location = data.PathDescription?.ExternalTableDescription?.Location;
48+
49+
return [
50+
...prepareExternalTableSummary(data, pathToDataSource),
51+
{
52+
label: i18n('external-objects.location'),
53+
value: (
54+
<EntityStatus
55+
name={location}
56+
showStatus={false}
57+
hasClipboardButton
58+
clipboardButtonAlwaysVisible
59+
className={b('location')}
60+
/>
61+
),
62+
},
63+
];
64+
};
65+
66+
interface ExternalTableProps {
67+
data?: TEvDescribeSchemeResult;
68+
prepareData: (data: TEvDescribeSchemeResult, pathToDataSource: string) => InfoViewerItem[];
69+
}
70+
71+
const ExternalTable = ({data, prepareData}: ExternalTableProps) => {
72+
const location = useLocation();
73+
const query = parseQuery(location);
74+
75+
// embedded version could be located in some folder (e.g. host/some_folder/app_router_path)
76+
// window.location has the full pathname, while location from router ignores path to project
77+
const pathToDataSource = createHref(window.location.pathname, undefined, {
78+
...query,
79+
schema: data?.PathDescription?.ExternalTableDescription?.DataSourcePath,
80+
});
81+
82+
const entityName = getEntityName(data?.PathDescription);
83+
84+
const {error: schemaError} = useTypedSelector((state) => state.schema);
85+
86+
if (schemaError) {
87+
return <ResponseError error={schemaError} />;
88+
}
89+
90+
if (!data) {
91+
return <div className="error">No {entityName} data</div>;
92+
}
93+
94+
return <InfoViewer title={entityName} info={prepareData(data, pathToDataSource)} />;
95+
};
96+
97+
export const ExternalTableInfo = ({data}: {data?: TEvDescribeSchemeResult}) => {
98+
return <ExternalTable data={data} prepareData={prepareExternalTableInfo} />;
99+
};
100+
101+
export const ExternalTableSummary = ({data}: {data?: TEvDescribeSchemeResult}) => {
102+
return <ExternalTable data={data} prepareData={prepareExternalTableSummary} />;
103+
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"external-objects.source-type": "Source Type",
3+
"external-objects.data-source": "Data Source",
4+
"external-objects.location": "Location",
5+
"external-objects.auth-method": "Auth Method",
6+
"external-objects.auth-method.none": "None",
7+
"external-objects.auth-method.service-account": "Service Account"
8+
}

0 commit comments

Comments
 (0)