Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,21 @@
public class KafkaConnectList extends BasePage {

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

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

@Step
public KafkaConnectList clickConnectorsTab() {
WebUtil.clickByJavaScript(connectorsTab);
return this;
}

@Step
public KafkaConnectList waitUntilScreenReady() {
clickConnectorsTab();
waitUntilSpinnerDisappear();
getPageTitleFromHeader(KAFKA_CONNECT).shouldBe(Condition.visible);
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public enum MenuItem {
TOPICS("Topics", "Topics"),
CONSUMERS("Consumers", "Consumers"),
SCHEMA_REGISTRY("Schema Registry", "Schema Registry"),
KAFKA_CONNECT("Kafka Connect", "Connectors"),
KAFKA_CONNECT("Kafka Connect", "Kafka Connect"),
KSQL_DB("KSQL DB", "KSQL DB");

private final String naviTitle;
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/src/main/java/io/kafbat/ui/variables/Url.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ public interface Url {
String TOPICS_LIST_URL = "http://%s:8080/ui/clusters/local/all-topics";
String CONSUMERS_LIST_URL = "http://%s:8080/ui/clusters/local/consumer-groups";
String SCHEMA_REGISTRY_LIST_URL = "http://%s:8080/ui/clusters/local/schemas";
String KAFKA_CONNECT_LIST_URL = "http://%s:8080/ui/clusters/local/connectors";
String KAFKA_CONNECT_LIST_URL = "http://%s:8080/ui/clusters/local/kafka-connect/connectors";
String KSQL_DB_LIST_URL = "http://%s:8080/ui/clusters/local/ksqldb/tables";
}
26 changes: 19 additions & 7 deletions frontend/src/components/ClusterPage/ClusterPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import useAppParams from 'lib/hooks/useAppParams';
import { ClusterFeaturesEnum } from 'generated-sources';
import {
clusterBrokerRelativePath,
clusterConnectorsRelativePath,
clusterConnectsRelativePath,
clusterConsumerGroupsRelativePath,
clusterKsqlDbRelativePath,
ClusterNameRoute,
Expand All @@ -14,16 +12,23 @@ import {
clusterConfigRelativePath,
getNonExactPath,
clusterAclRelativePath,
kafkaConnectRelativePath,
clusterConnectorNewRelativePath,
clusterConnectConnectorRelativePath,
} from 'lib/paths';
import ClusterContext from 'components/contexts/ClusterContext';
import PageLoader from 'components/common/PageLoader/PageLoader';
import { useClusters } from 'lib/hooks/api/clusters';
import { GlobalSettingsContext } from 'components/contexts/GlobalSettingsContext';
import New from 'components/Connect/New/New';
import SuspenseQueryComponent from 'components/common/SuspenseQueryComponent/SuspenseQueryComponent';
import DetailsPage from 'components/Connect/Details/DetailsPage';

const Brokers = React.lazy(() => import('components/Brokers/Brokers'));
const Topics = React.lazy(() => import('components/Topics/Topics'));
const Schemas = React.lazy(() => import('components/Schemas/Schemas'));
const Connect = React.lazy(() => import('components/Connect/Connect'));
const KafkaConnect = React.lazy(() => import('components/Connect/Connect'));

const KsqlDb = React.lazy(() => import('components/KsqlDb/KsqlDb'));
const ClusterConfigPage = React.lazy(
() => import('components/ClusterPage/ClusterConfigPage')
Expand Down Expand Up @@ -82,16 +87,23 @@ const ClusterPage: React.FC = () => {
element={<Schemas />}
/>
)}
{contextValue.hasKafkaConnectConfigured && (
<Route path={clusterConnectorNewRelativePath} element={<New />} />
)}
{contextValue.hasKafkaConnectConfigured && (
<Route
path={getNonExactPath(clusterConnectsRelativePath)}
element={<Connect />}
path={getNonExactPath(clusterConnectConnectorRelativePath)}
element={
<SuspenseQueryComponent>
<DetailsPage />
</SuspenseQueryComponent>
}
/>
)}
{contextValue.hasKafkaConnectConfigured && (
<Route
path={getNonExactPath(clusterConnectorsRelativePath)}
element={<Connect />}
path={getNonExactPath(kafkaConnectRelativePath)}
element={<KafkaConnect />}
/>
)}
{contextValue.hasKsqlDbConfigured && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@ import { render, WithRoute } from 'lib/testHelpers';
import {
clusterBrokersPath,
clusterConnectorsPath,
clusterConnectsPath,
clusterConsumerGroupsPath,
clusterKsqlDbPath,
clusterPath,
clusterSchemasPath,
clusterTopicsPath,
kafkaConnectPath,
} from 'lib/paths';
import { useClusters } from 'lib/hooks/api/clusters';
import { onlineClusterPayload } from 'lib/fixtures/clusters';

const CLusterCompText = {
Topics: 'Topics',
Schemas: 'Schemas',
Connect: 'Connect',
Connect: 'Kafka Connect',
Brokers: 'Brokers',
ConsumerGroups: 'ConsumerGroups',
KsqlDb: 'KsqlDb',
Expand Down Expand Up @@ -111,7 +111,7 @@ describe('ClusterPage', () => {
itCorrectlyHandlesConfiguredSchema(
ClusterFeaturesEnum.KAFKA_CONNECT,
CLusterCompText.Connect,
clusterConnectsPath(onlineClusterPayload.name)
kafkaConnectPath(onlineClusterPayload.name)
);
itCorrectlyHandlesConfiguredSchema(
ClusterFeaturesEnum.KAFKA_CONNECT,
Expand Down
20 changes: 20 additions & 0 deletions frontend/src/components/Connect/Clusters/Clusters.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import { useConnects } from 'lib/hooks/api/kafkaConnect';
import useAppParams from 'lib/hooks/useAppParams';
import { ClusterNameRoute } from 'lib/paths';

import ClustersStatistics from './ui/Statistics/Statistics';
import List from './ui/List/List';

const KafkaConnectClustersPage = () => {
const { clusterName } = useAppParams<ClusterNameRoute>();
const { data: connects, isLoading } = useConnects(clusterName, true);
return (
<>
<ClustersStatistics connects={connects ?? []} isLoading={isLoading} />
<List connects={connects ?? []} />
</>
);
};

export default KafkaConnectClustersPage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import AlertBadge from 'components/common/AlertBadge/AlertBadge';
import { Connect } from 'generated-sources';
import React from 'react';

type Props = { connect: Connect };
const ConnectorsCell = ({ connect }: Props) => {
const count = connect.connectorsCount ?? 0;
const failedCount = connect.failedConnectorsCount ?? 0;
const text = `${count - failedCount}/${count}`;

if (count === 0) {
return null;
}

if (failedCount > 0) {
return (
<AlertBadge>
<AlertBadge.Content content={text} />
<AlertBadge.Icon />
</AlertBadge>
);
}

return text;
};
export default ConnectorsCell;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { CellContext } from '@tanstack/react-table';
import { Connect } from 'generated-sources';
import React from 'react';

type Props = CellContext<Connect, string>;
const NameCell = ({ getValue }: Props) => {
return <div>{getValue()}</div>;
};
export default NameCell;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import AlertBadge from 'components/common/AlertBadge/AlertBadge';
import { Connect } from 'generated-sources';
import React from 'react';

type Props = { connect: Connect };
const TasksCell = ({ connect }: Props) => {
const count = connect.tasksCount ?? 0;
const failedCount = connect.failedTasksCount ?? 0;
const text = `${count - failedCount}/${count}`;

if (!count) {
return null;
}

if (failedCount > 0) {
return (
<AlertBadge>
<AlertBadge.Content content={text} />
<AlertBadge.Icon />
</AlertBadge>
);
}

return <div>{text}</div>;
};
export default TasksCell;
56 changes: 56 additions & 0 deletions frontend/src/components/Connect/Clusters/ui/List/List.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react';
import { Connect } from 'generated-sources';
import Table from 'components/common/NewTable';
import useAppParams from 'lib/hooks/useAppParams';
import { ClusterName } from 'lib/interfaces/cluster';
import { useNavigate } from 'react-router-dom';
import { clusterConnectorsPath } from 'lib/paths';
import { createColumnHelper } from '@tanstack/react-table';

import ConnectorsCell from './Cells/ConnectorsCell';
import NameCell from './Cells/NameCell';
import TasksCell from './Cells/TasksCell';

const helper = createColumnHelper<Connect>();
export const columns = [
helper.accessor('name', { cell: NameCell, size: 600 }),
helper.accessor('version', {
header: 'Version',
cell: ({ getValue }) => getValue(),
enableSorting: true,
}),
helper.display({
header: 'Connectors',
id: 'connectors',
cell: (props) => <ConnectorsCell connect={props.row.original} />,
size: 100,
}),
helper.display({
header: 'Running tasks',
id: 'tasks',
cell: (props) => <TasksCell connect={props.row.original} />,
size: 100,
}),
];

interface Props {
connects: Connect[];
}
const List = ({ connects }: Props) => {
const navigate = useNavigate();
const { clusterName } = useAppParams<{ clusterName: ClusterName }>();

return (
<Table
data={connects}
columns={columns}
onRowClick={({ original: { name } }) => {
navigate(`${clusterConnectorsPath(clusterName)}?connect=${name}`);
}}
emptyMessage="No kafka connect clusters"
enableSorting
/>
);
};

export default List;
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, { useMemo } from 'react';
import { Connect } from 'generated-sources';
import * as Statistics from 'components/common/Statistics';

import { computeStatistic } from './models/computeStatistics';

type Props = { connects: Connect[]; isLoading: boolean };
const ClustersStatistics = ({ connects, isLoading }: Props) => {
const statistic = useMemo(() => {
return computeStatistic(connects);
}, [connects]);
return (
<Statistics.Container>
<Statistics.Item
title="Clusters"
count={statistic.clustersCount}
isLoading={isLoading}
/>
<Statistics.Item
title="Connectors"
count={statistic.connectorsCount}
warningCount={statistic.failedConnectorsCount}
isLoading={isLoading}
/>
<Statistics.Item
title="Tasks"
count={statistic.tasksCount}
warningCount={statistic.failedTasksCount}
isLoading={isLoading}
/>
</Statistics.Container>
);
};

export default ClustersStatistics;
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Connect } from 'generated-sources';

interface Statistic {
clustersCount: number;
connectorsCount: number;
failedConnectorsCount: number;
tasksCount: number;
failedTasksCount: number;
}
export const computeStatistic = (connects: Connect[]): Statistic => {
const clustersCount = connects.length;
let connectorsCount = 0;
let failedConnectorsCount = 0;
let tasksCount = 0;
let failedTasksCount = 0;

connects.forEach((connect) => {
connectorsCount += connect.connectorsCount ?? 0;
failedConnectorsCount += connect.failedConnectorsCount ?? 0;
tasksCount += connect.tasksCount ?? 0;
failedTasksCount += connect.failedTasksCount ?? 0;
});

return {
clustersCount,
connectorsCount,
failedConnectorsCount,
tasksCount,
failedTasksCount,
};
};
Loading
Loading