Skip to content

Commit a04d6c8

Browse files
Merge branch 'main' into feature/connector-failed-modal
2 parents ada049d + c7b7b72 commit a04d6c8

File tree

7 files changed

+124
-52
lines changed

7 files changed

+124
-52
lines changed

api/src/main/java/io/kafbat/ui/model/rbac/permission/TopicAction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public enum TopicAction implements PermissibleAction {
1212
DELETE(VIEW),
1313
MESSAGES_READ(VIEW),
1414
MESSAGES_PRODUCE(VIEW),
15-
MESSAGES_DELETE(VIEW, EDIT),
15+
MESSAGES_DELETE(VIEW),
1616
ANALYSIS_VIEW(VIEW),
1717
ANALYSIS_RUN(VIEW, ANALYSIS_VIEW),
1818

e2e-playwright/src/services/commonFunctions.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { v4 as uuidv4 } from 'uuid';
2-
import { Page } from "@playwright/test";
2+
import { expect, Locator, Page } from "@playwright/test";
33

44
export const generateName = (prefix: string): string => {
55
return `${prefix}${uuidv4().slice(0, 8)}`;
@@ -17,6 +17,30 @@ export async function clearWithSelectAll(page: Page): Promise<void> {
1717
await page.keyboard.press('Backspace');
1818
}
1919

20-
// export const generateName = (prefix: string): string => {
21-
// return `${prefix}-${uuidv4().slice(0, 8)}`;
22-
// };
20+
export async function clickMenuThenItem(
21+
page: Page,
22+
expectable : Locator,
23+
result: Locator
24+
): Promise<void> {
25+
const VISIBLE_TIMEOUT = 5000;
26+
const ENABLED_TIMEOUT = 5000;
27+
const NETWORK_IDLE_TIMEOUT = 10000;
28+
29+
async function attemptClick() {
30+
await expect(expectable).toBeVisible({ timeout: VISIBLE_TIMEOUT });
31+
await expect(expectable).toBeEnabled({ timeout: ENABLED_TIMEOUT });
32+
await expectable.scrollIntoViewIfNeeded();
33+
await expectable.click();
34+
35+
await expect(result).toBeVisible({ timeout: VISIBLE_TIMEOUT });
36+
await result.click();
37+
}
38+
39+
try {
40+
await attemptClick();
41+
} catch {
42+
await page.reload({ waitUntil: 'domcontentloaded' });
43+
await page.waitForLoadState('networkidle', { timeout: NETWORK_IDLE_TIMEOUT }).catch(() => {});
44+
await attemptClick();
45+
}
46+
}

e2e-playwright/src/steps/KafkaConnect.steps.ts

Lines changed: 11 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { Given, When, Then, setDefaultTimeout } from "@cucumber/cucumber";
33
import { expectVisibility } from "../services/uiHelper";
44
import { PlaywrightWorld } from "../support/PlaywrightWorld";
5-
import { expect } from "@playwright/test";
5+
import { clickMenuThenItem } from "../services/commonFunctions";
66

77
setDefaultTimeout(60 * 1000 * 2);
88

@@ -26,29 +26,11 @@ Given('KafkaConnect satus is: {string}, type is: {string}', async function(this:
2626
});
2727

2828
Given('KafkaConnect row menu menu item {string} is clicked', async function(this: PlaywrightWorld, menuItem: string) {
29-
const { page } = this;
30-
const rowMenu = this.locators.connectors.rowMenu;
31-
const rowMenuItem = this.locators.connectors.rowMenuItem(menuItem);
32-
33-
async function attemptClick() {
34-
await expect(rowMenu).toBeVisible({ timeout: 5000 });
35-
await expect(rowMenu).toBeEnabled({ timeout: 5000 });
36-
await rowMenu.scrollIntoViewIfNeeded();
37-
38-
await rowMenu.click();
39-
40-
await expect(rowMenuItem).toBeVisible({ timeout: 5000 });
41-
await rowMenuItem.click();
42-
}
43-
44-
try {
45-
await attemptClick();
46-
} catch (firstErr) {
47-
await page.reload({ waitUntil: 'domcontentloaded' });
48-
await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {});
49-
50-
await attemptClick();
51-
}
29+
await clickMenuThenItem(
30+
this.page,
31+
this.locators.connectors.rowMenu,
32+
this.locators.connectors.rowMenuItem(menuItem)
33+
);
5234
});
5335

5436
When('KafkaConnect connector named: {string} clicked', async function(this: PlaywrightWorld, name: string) {
@@ -64,29 +46,11 @@ Given('KafkaConnect connector page status is: {string}', async function(this: Pl
6446
});
6547

6648
When('KafkaConnect connector menu item {string} clicked', async function(this: PlaywrightWorld, menuItem: string) {
67-
const { page } = this;
68-
const menuButton = this.locators.connectors.internalMenuButton;
69-
const menuItemLocator = this.locators.connectors.internalMenuButtonItem(menuItem);
70-
71-
async function attemptClick() {
72-
await expect(menuButton).toBeVisible({ timeout: 5000 });
73-
await expect(menuButton).toBeEnabled({ timeout: 5000 });
74-
75-
await menuButton.click();
76-
77-
await expect(menuItemLocator).toBeVisible({ timeout: 5000 });
78-
await menuItemLocator.click();
79-
}
80-
81-
try {
82-
await attemptClick();
83-
} catch (firstErr) {
84-
await page.reload({ waitUntil: 'domcontentloaded' });
85-
86-
await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {});
87-
88-
await attemptClick();
89-
}
49+
await clickMenuThenItem(
50+
this.page,
51+
this.locators.connectors.internalMenuButton,
52+
this.locators.connectors.internalMenuButtonItem(menuItem)
53+
);
9054
});
9155

9256
Given('KafkaConnect connector page state is: {string}', async function(this: PlaywrightWorld, state: string) {

frontend/src/components/Connect/Details/DetailsPage.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import {
55
clusterConnectConnectorConfigPath,
66
clusterConnectConnectorConfigRelativePath,
77
clusterConnectConnectorPath,
8+
clusterConnectConnectorTopicsPath,
9+
clusterConnectConnectorTopicsRelativePath,
810
clusterConnectorsPath,
911
RouterParamsClusterConnectConnector,
1012
} from 'lib/paths';
@@ -16,6 +18,7 @@ import Overview from './Overview/Overview';
1618
import Tasks from './Tasks/Tasks';
1719
import Config from './Config/Config';
1820
import Actions from './Actions/Actions';
21+
import Topics from './Topics/Topics';
1922

2023
const DetailsPage: React.FC = () => {
2124
const { clusterName, connectName, connectorName } =
@@ -53,6 +56,16 @@ const DetailsPage: React.FC = () => {
5356
>
5457
Config
5558
</NavLink>
59+
<NavLink
60+
to={clusterConnectConnectorTopicsPath(
61+
clusterName,
62+
connectName,
63+
connectorName
64+
)}
65+
className={({ isActive }) => (isActive ? 'is-active' : '')}
66+
>
67+
Topics
68+
</NavLink>
5669
</Navbar>
5770
<Suspense fallback={<PageLoader />}>
5871
<Routes>
@@ -61,6 +74,10 @@ const DetailsPage: React.FC = () => {
6174
path={clusterConnectConnectorConfigRelativePath}
6275
element={<Config />}
6376
/>
77+
<Route
78+
path={clusterConnectConnectorTopicsRelativePath}
79+
element={<Topics />}
80+
/>
6481
</Routes>
6582
</Suspense>
6683
</div>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from 'react';
2+
import useAppParams from 'lib/hooks/useAppParams';
3+
import { RouterParamsClusterConnectConnector } from 'lib/paths';
4+
import { useConnector } from 'lib/hooks/api/kafkaConnect';
5+
import Table from 'components/common/NewTable';
6+
import { ColumnDef } from '@tanstack/react-table';
7+
8+
import { TopicNameCell } from './cells/TopicNameCell';
9+
10+
const columns: ColumnDef<{ topicName: string }>[] = [
11+
{
12+
header: 'Topic',
13+
accessorKey: 'topicName',
14+
cell: TopicNameCell,
15+
},
16+
];
17+
18+
const Topics = () => {
19+
const routerProps = useAppParams<RouterParamsClusterConnectConnector>();
20+
21+
const { data: connector } = useConnector(routerProps);
22+
23+
const tableData = (connector?.topics ?? []).map((topicName) => ({
24+
topicName,
25+
}));
26+
27+
return (
28+
<Table columns={columns} data={tableData} emptyMessage="No topics found" />
29+
);
30+
};
31+
32+
export default Topics;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react';
2+
import useAppParams from 'lib/hooks/useAppParams';
3+
import {
4+
clusterTopicPath,
5+
RouterParamsClusterConnectConnector,
6+
} from 'lib/paths';
7+
import { CellContext } from '@tanstack/react-table';
8+
import { TableKeyLink } from 'components/common/table/Table/TableKeyLink.styled';
9+
import { Link } from 'react-router-dom';
10+
11+
type TopicNameCellProps = CellContext<{ topicName: string }, unknown>;
12+
13+
export const TopicNameCell = ({ getValue }: TopicNameCellProps) => {
14+
const routerProps = useAppParams<RouterParamsClusterConnectConnector>();
15+
const topicName = getValue<string>();
16+
17+
return (
18+
<TableKeyLink>
19+
<Link to={clusterTopicPath(routerProps.clusterName, topicName)}>
20+
{topicName}
21+
</Link>
22+
</TableKeyLink>
23+
);
24+
};

frontend/src/lib/paths.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ export const clusterConnectConnectorRelativePath = `${clusterConnectsRelativePat
216216
export const clusterConnectorNewRelativePath = 'create-new';
217217
const clusterConnectConnectorTasksRelativePath = 'tasks';
218218
export const clusterConnectConnectorConfigRelativePath = 'config';
219+
export const clusterConnectConnectorTopicsRelativePath = 'topics';
219220

220221
export const clusterConnectsPath = (
221222
clusterName: ClusterName = RouteParams.clusterName
@@ -281,6 +282,16 @@ export const clusterConnectConnectorConfigPath = (
281282
connectName,
282283
connectorName
283284
)}/${clusterConnectConnectorConfigRelativePath}`;
285+
export const clusterConnectConnectorTopicsPath = (
286+
clusterName: ClusterName = RouteParams.clusterName,
287+
connectName: Connect['name'] = RouteParams.connectName,
288+
connectorName: Connector['name'] = RouteParams.connectorName
289+
) =>
290+
`${clusterConnectConnectorPath(
291+
clusterName,
292+
connectName,
293+
connectorName
294+
)}/${clusterConnectConnectorTopicsRelativePath}`;
284295

285296
export type RouterParamsClusterConnectConnector = {
286297
clusterName: ClusterName;

0 commit comments

Comments
 (0)