Skip to content

Commit 69cb9aa

Browse files
Vixtirgermanosin
andauthored
Issue/445 Kafka connect overview (#1232)
Co-authored-by: German Osin <german.osin@gmail.com>
1 parent 40984bf commit 69cb9aa

File tree

35 files changed

+801
-309
lines changed

35 files changed

+801
-309
lines changed

e2e-tests/src/main/java/io/kafbat/ui/screens/connectors/KafkaConnectList.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,21 @@
1313
public class KafkaConnectList extends BasePage {
1414

1515
protected SelenideElement createConnectorBtn = $x("//button[contains(text(),'Create Connector')]");
16+
protected SelenideElement connectorsTab = $x("//a[contains(text(),'Connectors')]");
1617

1718
public KafkaConnectList() {
1819
tableElementNameLocator = "//tbody//td[contains(text(),'%s')]";
1920
}
2021

22+
@Step
23+
public KafkaConnectList clickConnectorsTab() {
24+
WebUtil.clickByJavaScript(connectorsTab);
25+
return this;
26+
}
27+
2128
@Step
2229
public KafkaConnectList waitUntilScreenReady() {
30+
clickConnectorsTab();
2331
waitUntilSpinnerDisappear();
2432
getPageTitleFromHeader(KAFKA_CONNECT).shouldBe(Condition.visible);
2533
return this;

e2e-tests/src/main/java/io/kafbat/ui/screens/panels/enums/MenuItem.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public enum MenuItem {
1010
TOPICS("Topics", "Topics"),
1111
CONSUMERS("Consumers", "Consumers"),
1212
SCHEMA_REGISTRY("Schema Registry", "Schema Registry"),
13-
KAFKA_CONNECT("Kafka Connect", "Connectors"),
13+
KAFKA_CONNECT("Kafka Connect", "Kafka Connect"),
1414
KSQL_DB("KSQL DB", "KSQL DB");
1515

1616
private final String naviTitle;

e2e-tests/src/main/java/io/kafbat/ui/variables/Url.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ public interface Url {
66
String TOPICS_LIST_URL = "http://%s:8080/ui/clusters/local/all-topics";
77
String CONSUMERS_LIST_URL = "http://%s:8080/ui/clusters/local/consumer-groups";
88
String SCHEMA_REGISTRY_LIST_URL = "http://%s:8080/ui/clusters/local/schemas";
9-
String KAFKA_CONNECT_LIST_URL = "http://%s:8080/ui/clusters/local/connectors";
9+
String KAFKA_CONNECT_LIST_URL = "http://%s:8080/ui/clusters/local/kafka-connect/connectors";
1010
String KSQL_DB_LIST_URL = "http://%s:8080/ui/clusters/local/ksqldb/tables";
1111
}

frontend/src/components/ClusterPage/ClusterPage.tsx

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import useAppParams from 'lib/hooks/useAppParams';
44
import { ClusterFeaturesEnum } from 'generated-sources';
55
import {
66
clusterBrokerRelativePath,
7-
clusterConnectorsRelativePath,
8-
clusterConnectsRelativePath,
97
clusterConsumerGroupsRelativePath,
108
clusterKsqlDbRelativePath,
119
ClusterNameRoute,
@@ -14,16 +12,23 @@ import {
1412
clusterConfigRelativePath,
1513
getNonExactPath,
1614
clusterAclRelativePath,
15+
kafkaConnectRelativePath,
16+
clusterConnectorNewRelativePath,
17+
clusterConnectConnectorRelativePath,
1718
} from 'lib/paths';
1819
import ClusterContext from 'components/contexts/ClusterContext';
1920
import PageLoader from 'components/common/PageLoader/PageLoader';
2021
import { useClusters } from 'lib/hooks/api/clusters';
2122
import { GlobalSettingsContext } from 'components/contexts/GlobalSettingsContext';
23+
import New from 'components/Connect/New/New';
24+
import SuspenseQueryComponent from 'components/common/SuspenseQueryComponent/SuspenseQueryComponent';
25+
import DetailsPage from 'components/Connect/Details/DetailsPage';
2226

2327
const Brokers = React.lazy(() => import('components/Brokers/Brokers'));
2428
const Topics = React.lazy(() => import('components/Topics/Topics'));
2529
const Schemas = React.lazy(() => import('components/Schemas/Schemas'));
26-
const Connect = React.lazy(() => import('components/Connect/Connect'));
30+
const KafkaConnect = React.lazy(() => import('components/Connect/Connect'));
31+
2732
const KsqlDb = React.lazy(() => import('components/KsqlDb/KsqlDb'));
2833
const ClusterConfigPage = React.lazy(
2934
() => import('components/ClusterPage/ClusterConfigPage')
@@ -82,16 +87,23 @@ const ClusterPage: React.FC = () => {
8287
element={<Schemas />}
8388
/>
8489
)}
90+
{contextValue.hasKafkaConnectConfigured && (
91+
<Route path={clusterConnectorNewRelativePath} element={<New />} />
92+
)}
8593
{contextValue.hasKafkaConnectConfigured && (
8694
<Route
87-
path={getNonExactPath(clusterConnectsRelativePath)}
88-
element={<Connect />}
95+
path={getNonExactPath(clusterConnectConnectorRelativePath)}
96+
element={
97+
<SuspenseQueryComponent>
98+
<DetailsPage />
99+
</SuspenseQueryComponent>
100+
}
89101
/>
90102
)}
91103
{contextValue.hasKafkaConnectConfigured && (
92104
<Route
93-
path={getNonExactPath(clusterConnectorsRelativePath)}
94-
element={<Connect />}
105+
path={getNonExactPath(kafkaConnectRelativePath)}
106+
element={<KafkaConnect />}
95107
/>
96108
)}
97109
{contextValue.hasKsqlDbConfigured && (

frontend/src/components/ClusterPage/__tests__/ClusterPage.spec.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,20 @@ import { render, WithRoute } from 'lib/testHelpers';
66
import {
77
clusterBrokersPath,
88
clusterConnectorsPath,
9-
clusterConnectsPath,
109
clusterConsumerGroupsPath,
1110
clusterKsqlDbPath,
1211
clusterPath,
1312
clusterSchemasPath,
1413
clusterTopicsPath,
14+
kafkaConnectPath,
1515
} from 'lib/paths';
1616
import { useClusters } from 'lib/hooks/api/clusters';
1717
import { onlineClusterPayload } from 'lib/fixtures/clusters';
1818

1919
const CLusterCompText = {
2020
Topics: 'Topics',
2121
Schemas: 'Schemas',
22-
Connect: 'Connect',
22+
Connect: 'Kafka Connect',
2323
Brokers: 'Brokers',
2424
ConsumerGroups: 'ConsumerGroups',
2525
KsqlDb: 'KsqlDb',
@@ -111,7 +111,7 @@ describe('ClusterPage', () => {
111111
itCorrectlyHandlesConfiguredSchema(
112112
ClusterFeaturesEnum.KAFKA_CONNECT,
113113
CLusterCompText.Connect,
114-
clusterConnectsPath(onlineClusterPayload.name)
114+
kafkaConnectPath(onlineClusterPayload.name)
115115
);
116116
itCorrectlyHandlesConfiguredSchema(
117117
ClusterFeaturesEnum.KAFKA_CONNECT,
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React from 'react';
2+
import { useConnects } from 'lib/hooks/api/kafkaConnect';
3+
import useAppParams from 'lib/hooks/useAppParams';
4+
import { ClusterNameRoute } from 'lib/paths';
5+
6+
import ClustersStatistics from './ui/Statistics/Statistics';
7+
import List from './ui/List/List';
8+
9+
const KafkaConnectClustersPage = () => {
10+
const { clusterName } = useAppParams<ClusterNameRoute>();
11+
const { data: connects, isLoading } = useConnects(clusterName, true);
12+
return (
13+
<>
14+
<ClustersStatistics connects={connects ?? []} isLoading={isLoading} />
15+
<List connects={connects ?? []} />
16+
</>
17+
);
18+
};
19+
20+
export default KafkaConnectClustersPage;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import AlertBadge from 'components/common/AlertBadge/AlertBadge';
2+
import { Connect } from 'generated-sources';
3+
import React from 'react';
4+
5+
type Props = { connect: Connect };
6+
const ConnectorsCell = ({ connect }: Props) => {
7+
const count = connect.connectorsCount ?? 0;
8+
const failedCount = connect.failedConnectorsCount ?? 0;
9+
const text = `${count - failedCount}/${count}`;
10+
11+
if (count === 0) {
12+
return null;
13+
}
14+
15+
if (failedCount > 0) {
16+
return (
17+
<AlertBadge>
18+
<AlertBadge.Content content={text} />
19+
<AlertBadge.Icon />
20+
</AlertBadge>
21+
);
22+
}
23+
24+
return text;
25+
};
26+
export default ConnectorsCell;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { CellContext } from '@tanstack/react-table';
2+
import { Connect } from 'generated-sources';
3+
import React from 'react';
4+
5+
type Props = CellContext<Connect, string>;
6+
const NameCell = ({ getValue }: Props) => {
7+
return <div>{getValue()}</div>;
8+
};
9+
export default NameCell;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import AlertBadge from 'components/common/AlertBadge/AlertBadge';
2+
import { Connect } from 'generated-sources';
3+
import React from 'react';
4+
5+
type Props = { connect: Connect };
6+
const TasksCell = ({ connect }: Props) => {
7+
const count = connect.tasksCount ?? 0;
8+
const failedCount = connect.failedTasksCount ?? 0;
9+
const text = `${count - failedCount}/${count}`;
10+
11+
if (!count) {
12+
return null;
13+
}
14+
15+
if (failedCount > 0) {
16+
return (
17+
<AlertBadge>
18+
<AlertBadge.Content content={text} />
19+
<AlertBadge.Icon />
20+
</AlertBadge>
21+
);
22+
}
23+
24+
return <div>{text}</div>;
25+
};
26+
export default TasksCell;
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import React from 'react';
2+
import { Connect } from 'generated-sources';
3+
import Table from 'components/common/NewTable';
4+
import useAppParams from 'lib/hooks/useAppParams';
5+
import { ClusterName } from 'lib/interfaces/cluster';
6+
import { useNavigate } from 'react-router-dom';
7+
import { clusterConnectorsPath } from 'lib/paths';
8+
import { createColumnHelper } from '@tanstack/react-table';
9+
10+
import ConnectorsCell from './Cells/ConnectorsCell';
11+
import NameCell from './Cells/NameCell';
12+
import TasksCell from './Cells/TasksCell';
13+
14+
const helper = createColumnHelper<Connect>();
15+
export const columns = [
16+
helper.accessor('name', { cell: NameCell, size: 600 }),
17+
helper.accessor('version', {
18+
header: 'Version',
19+
cell: ({ getValue }) => getValue(),
20+
enableSorting: true,
21+
}),
22+
helper.display({
23+
header: 'Connectors',
24+
id: 'connectors',
25+
cell: (props) => <ConnectorsCell connect={props.row.original} />,
26+
size: 100,
27+
}),
28+
helper.display({
29+
header: 'Running tasks',
30+
id: 'tasks',
31+
cell: (props) => <TasksCell connect={props.row.original} />,
32+
size: 100,
33+
}),
34+
];
35+
36+
interface Props {
37+
connects: Connect[];
38+
}
39+
const List = ({ connects }: Props) => {
40+
const navigate = useNavigate();
41+
const { clusterName } = useAppParams<{ clusterName: ClusterName }>();
42+
43+
return (
44+
<Table
45+
data={connects}
46+
columns={columns}
47+
onRowClick={({ original: { name } }) => {
48+
navigate(`${clusterConnectorsPath(clusterName)}?connect=${name}`);
49+
}}
50+
emptyMessage="No kafka connect clusters"
51+
enableSorting
52+
/>
53+
);
54+
};
55+
56+
export default List;

0 commit comments

Comments
 (0)