Skip to content

Commit cb5a755

Browse files
authored
feat: show transfer errors in UI (#2043)
1 parent 02cd0e8 commit cb5a755

File tree

6 files changed

+97
-4
lines changed

6 files changed

+97
-4
lines changed

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ function Overview({type, path, database}: OverviewProps) {
9595
[EPathType.EPathTypeExternalDataSource]: () => <ExternalDataSourceInfo data={data} />,
9696
[EPathType.EPathTypeView]: () => <ViewInfo data={data} />,
9797
[EPathType.EPathTypeReplication]: () => <AsyncReplicationInfo data={data} />,
98-
[EPathType.EPathTypeTransfer]: () => <TransferInfo data={data} />,
98+
[EPathType.EPathTypeTransfer]: () => (
99+
<TransferInfo path={path} database={database} data={data} />
100+
),
99101
};
100102

101103
return (type && pathTypeToComponent[type]?.()) || <TableInfo data={data} type={type} />;

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

+23-3
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,22 @@ import {Flex, Text} from '@gravity-ui/uikit';
44
import {AsyncReplicationState} from '../../../../../components/AsyncReplicationState';
55
import {YDBSyntaxHighlighter} from '../../../../../components/SyntaxHighlighter/YDBSyntaxHighlighter';
66
import {YDBDefinitionList} from '../../../../../components/YDBDefinitionList/YDBDefinitionList';
7+
import {replicationApi} from '../../../../../store/reducers/replication';
8+
import type {DescribeReplicationResult} from '../../../../../types/api/replication';
79
import type {TEvDescribeSchemeResult} from '../../../../../types/api/schema';
810
import {getEntityName} from '../../../utils';
911

1012
import {Credentials} from './Credentials';
1113
import i18n from './i18n';
1214

1315
interface TransferProps {
16+
path: string;
17+
database: string;
1418
data?: TEvDescribeSchemeResult;
1519
}
1620

1721
/** Displays overview for Transfer EPathType */
18-
export function TransferInfo({data}: TransferProps) {
22+
export function TransferInfo({path, database, data}: TransferProps) {
1923
const entityName = getEntityName(data?.PathDescription);
2024

2125
if (!data) {
@@ -26,7 +30,8 @@ export function TransferInfo({data}: TransferProps) {
2630
);
2731
}
2832

29-
const transferItems = prepareTransferItems(data);
33+
const {data: replicationData} = replicationApi.useGetReplicationQuery({path, database}, {});
34+
const transferItems = prepareTransferItems(data, replicationData);
3035

3136
return (
3237
<Flex direction="column" gap="4">
@@ -35,7 +40,10 @@ export function TransferInfo({data}: TransferProps) {
3540
);
3641
}
3742

38-
function prepareTransferItems(data: TEvDescribeSchemeResult) {
43+
function prepareTransferItems(
44+
data: TEvDescribeSchemeResult,
45+
replicationData: DescribeReplicationResult | undefined,
46+
) {
3947
const transferDescription = data.PathDescription?.ReplicationDescription || {};
4048
const state = transferDescription.State;
4149
const srcConnectionParams = transferDescription.Config?.SrcConnectionParams || {};
@@ -54,6 +62,18 @@ function prepareTransferItems(data: TEvDescribeSchemeResult) {
5462
});
5563
}
5664

65+
if (replicationData?.error?.issues && replicationData.error.issues[0]?.message) {
66+
info.push({
67+
name: i18n('state.error'),
68+
copyText: replicationData.error.issues[0].message,
69+
content: (
70+
<Text variant="code-inline-2" color="danger">
71+
{replicationData.error.issues[0].message}
72+
</Text>
73+
),
74+
});
75+
}
76+
5777
if (Endpoint) {
5878
info.push({
5979
name: i18n('srcConnection.endpoint.label'),

src/containers/Tenant/Diagnostics/Overview/TransferInfo/i18n/en.json

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"srcConnection.database.label": "Source Database Path",
55
"srcConnection.endpoint.label": "Source Cluster Endpoint",
66
"state.label": "State",
7+
"state.error": "Error",
78
"srcPath.label": "Source Topic",
89
"dstPath.label": "Destination Table",
910
"transformLambda.label": "Transformation Lambda"

src/services/api/viewer.ts

+17
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import type {
1818
SendQueryParams,
1919
} from '../../types/api/query';
2020
import type {JsonRenderRequestParams, JsonRenderResponse} from '../../types/api/render';
21+
import type {DescribeReplicationResult} from '../../types/api/replication';
2122
import type {TEvDescribeSchemeResult} from '../../types/api/schema';
2223
import type {StorageRequestParams, TStorageInfo} from '../../types/api/storage';
2324
import type {TEvSystemStateResponse} from '../../types/api/systemState';
@@ -229,6 +230,22 @@ export class ViewerAPI extends BaseYdbAPI {
229230
);
230231
}
231232

233+
getReplication(
234+
{path, database}: {path: string; database: string},
235+
{concurrentId, signal}: AxiosOptions = {},
236+
) {
237+
return this.get<DescribeReplicationResult>(
238+
this.getPath('/viewer/json/describe_replication'),
239+
{
240+
enums: true,
241+
include_stats: true,
242+
database,
243+
path,
244+
},
245+
{concurrentId, requestConfig: {signal}},
246+
);
247+
}
248+
232249
getTopic(
233250
{path, database}: {path: string; database: string},
234251
{concurrentId, signal}: AxiosOptions = {},

src/store/reducers/replication.ts

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import {api} from './api';
2+
3+
export const replicationApi = api.injectEndpoints({
4+
endpoints: (build) => ({
5+
getReplication: build.query({
6+
queryFn: async (params: {path: string; database: string}) => {
7+
try {
8+
const data = await window.api.viewer.getReplication(params);
9+
// On older version it can return HTML page of Developer UI with an error
10+
if (typeof data !== 'object') {
11+
return {error: {}};
12+
}
13+
return {data};
14+
} catch (error) {
15+
return {error};
16+
}
17+
},
18+
providesTags: ['All'],
19+
}),
20+
}),
21+
overrideExisting: 'throw',
22+
});

src/types/api/replication.ts

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/* eslint-disable camelcase */
2+
3+
import type {TIssueMessage} from './operations';
4+
5+
/**
6+
* endpoint: /json/describe_replication
7+
*
8+
* source: https://github.com/ydb-platform/ydb/blob/main/ydb/public/api/protos/draft/ydb_replication.proto
9+
*
10+
* Original proto file doesn't specify optional fields, so every field is considered optional
11+
*/
12+
export interface DescribeReplicationResult {
13+
items?: TReplicationItems;
14+
15+
error?: TReplicationErrorState;
16+
}
17+
18+
export interface TReplicationErrorState {
19+
issues?: TIssueMessage[];
20+
}
21+
22+
export interface TReplicationItems {
23+
items?: TReplicationItem[];
24+
}
25+
26+
export interface TReplicationItem {
27+
id?: string;
28+
source_path?: string;
29+
destination_path?: string;
30+
source_changefeed_name?: string;
31+
}

0 commit comments

Comments
 (0)