Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: [issues#121] Topic Details: Display consumers #448

Merged
merged 8 commits into from
May 14, 2021
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from 'react';
import { Topic, TopicDetails, ConsumerGroup } from 'generated-sources';
import { ClusterName, TopicName } from 'redux/interfaces';
import ListItem from 'components/ConsumerGroups/List/ListItem';

interface Props extends Topic, TopicDetails {
clusterName: ClusterName;
topicName: TopicName;
consumerGroups: Array<ConsumerGroup>;
fetchTopicConsumerGroups(
clusterName: ClusterName,
topicName: TopicName
): void;
}

const TopicConsumerGroups: React.FC<Props> = ({
consumerGroups,
fetchTopicConsumerGroups,
clusterName,
topicName,
}) => {
React.useEffect(() => {
fetchTopicConsumerGroups(clusterName, topicName);
}, []);

return (
<div className="box">
{consumerGroups.length > 0 ? (
<div>
<table className="table is-striped is-fullwidth is-hoverable">
<thead>
<tr>
<th>Consumer group ID</th>
<th>Num of consumers</th>
<th>Num of topics</th>
</tr>
</thead>
<tbody>
{consumerGroups.map((consumerGroup) => (
<ListItem
key={consumerGroup.consumerGroupId}
consumerGroup={consumerGroup}
/>
))}
</tbody>
</table>
</div>
) : (
'No active consumer groups'
)}
</div>
);
};

export default TopicConsumerGroups;
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { connect } from 'react-redux';
import { RootState, TopicName, ClusterName } from 'redux/interfaces';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { fetchTopicConsumerGroups } from 'redux/actions';
import TopicConsumerGroups from 'components/Topics/Topic/Details/ConsumerGroups/ConsumerGroups';
import { getTopicConsumerGroups } from 'redux/reducers/topics/selectors';

interface RouteProps {
clusterName: ClusterName;
topicName: TopicName;
}

type OwnProps = RouteComponentProps<RouteProps>;

const mapStateToProps = (
state: RootState,
{
match: {
params: { topicName, clusterName },
},
}: OwnProps
) => {
return {
consumerGroups: { ...getTopicConsumerGroups(state) },
workshur marked this conversation as resolved.
Show resolved Hide resolved
topicName,
clusterName,
};
};

const mapDispatchToProps = {
fetchTopicConsumerGroups,
};

export default withRouter(
connect(mapStateToProps, mapDispatchToProps)(TopicConsumerGroups)
);
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import {
clusterTopicPath,
clusterTopicMessagesPath,
clusterTopicsPath,
clusterTopicConsumerGroupsPath,
clusterTopicEditPath,
} from 'lib/paths';
import ClusterContext from 'components/contexts/ClusterContext';
import ConfirmationModal from 'components/common/ConfirmationModal/ConfirmationModal';

import OverviewContainer from './Overview/OverviewContainer';
import TopicConsumerGroupsContainer from './ConsumerGroups/ConsumerGroupsContainer';
import MessagesContainer from './Messages/MessagesContainer';
import SettingsContainer from './Settings/SettingsContainer';

Expand Down Expand Up @@ -64,6 +66,14 @@ const Details: React.FC<Props> = ({
>
Messages
</NavLink>
<NavLink
exact
to={clusterTopicConsumerGroupsPath(clusterName, topicName)}
className="navbar-item is-tab"
activeClassName="is-active"
>
Consumers
</NavLink>
<NavLink
exact
to={clusterTopicSettingsPath(clusterName, topicName)}
Expand Down Expand Up @@ -128,6 +138,11 @@ const Details: React.FC<Props> = ({
path="/ui/clusters/:clusterName/topics/:topicName"
component={OverviewContainer}
/>
<Route
exact
path="/ui/clusters/:clusterName/topics/:topicName/consumergroups"
component={TopicConsumerGroupsContainer}
/>
</Switch>
</div>
);
Expand Down
4 changes: 4 additions & 0 deletions kafka-ui-react-app/src/lib/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ export const clusterTopicEditPath = (
clusterName: ClusterName,
topicName: TopicName
) => `${clusterTopicsPath(clusterName)}/${topicName}/edit`;
export const clusterTopicConsumerGroupsPath = (
clusterName: ClusterName,
topicName: TopicName
) => `${clusterTopicsPath(clusterName)}/${topicName}/consumergroups`;

// Kafka Connect
export const clusterConnectorsPath = (clusterName: ClusterName) =>
Expand Down
6 changes: 6 additions & 0 deletions kafka-ui-react-app/src/redux/actions/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,9 @@ export const deleteConnectorAction = createAsyncAction(
'DELETE_CONNECTOR__SUCCESS',
'DELETE_CONNECTOR__FAILURE'
)<undefined, ConnectState, { alert?: FailurePayload }>();

export const fetchTopicConsumerGroupsAction = createAsyncAction(
'GET_TOPIC_CONSUMER_GROUPS__REQUEST',
'GET_TOPIC_CONSUMER_GROUPS__SUCCESS',
'GET_TOPIC_CONSUMER_GROUPS__FAILURE'
)<undefined, TopicsState, undefined>();
35 changes: 35 additions & 0 deletions kafka-ui-react-app/src/redux/actions/thunks/topics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
TopicCreation,
TopicUpdate,
TopicConfig,
ConsumerGroupsApi,
} from 'generated-sources';
import {
PromiseThunkResult,
Expand All @@ -25,6 +26,9 @@ import { getResponse } from 'lib/errorHandling';
const apiClientConf = new Configuration(BASE_PARAMS);
export const topicsApiClient = new TopicsApi(apiClientConf);
export const messagesApiClient = new MessagesApi(apiClientConf);
export const topicConsumerGroupsApiClient = new ConsumerGroupsApi(
apiClientConf
);

export interface FetchTopicsListParams {
clusterName: ClusterName;
Expand Down Expand Up @@ -310,3 +314,34 @@ export const deleteTopic = (
dispatch(actions.deleteTopicAction.failure());
}
};

export const fetchTopicConsumerGroups = (
clusterName: ClusterName,
topicName: TopicName
): PromiseThunkResult => async (dispatch, getState) => {
dispatch(actions.fetchTopicConsumerGroupsAction.request());
try {
const topicDetails = await topicConsumerGroupsApiClient.getTopicConsumerGroups(
workshur marked this conversation as resolved.
Show resolved Hide resolved
{
clusterName,
topicName,
}
);
const state = getState().topics;
const newState = {
...state,
byName: {
...state.byName,
[topicName]: {
...state.byName[topicName],
consumerGroups: {
...topicDetails,
},
},
},
};
dispatch(actions.fetchTopicConsumerGroupsAction.success(newState));
} catch (e) {
dispatch(actions.fetchTopicConsumerGroupsAction.failure());
}
};
2 changes: 2 additions & 0 deletions kafka-ui-react-app/src/redux/interfaces/topic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
TopicConfig,
TopicCreation,
GetTopicMessagesRequest,
ConsumerGroup,
} from 'generated-sources';

export type TopicName = Topic['name'];
Expand Down Expand Up @@ -45,6 +46,7 @@ export interface TopicsState {
allNames: TopicName[];
totalPages: number;
messages: TopicMessage[];
consumerGroups: ConsumerGroup[];
}

export type TopicFormFormattedParams = TopicCreation['configs'];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const state = {
allNames: [topic.name],
messages: [],
totalPages: 1,
consumerGroups: [],
};

describe('topics reducer', () => {
Expand All @@ -22,6 +23,7 @@ describe('topics reducer', () => {
allNames: [],
messages: [],
totalPages: 1,
consumerGroups: [],
});
});

Expand Down
2 changes: 2 additions & 0 deletions kafka-ui-react-app/src/redux/reducers/topics/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const initialState: TopicsState = {
allNames: [],
totalPages: 1,
messages: [],
consumerGroups: [],
};

const transformTopicMessages = (
Expand Down Expand Up @@ -41,6 +42,7 @@ const reducer = (state = initialState, action: Action): TopicsState => {
case getType(actions.fetchTopicDetailsAction.success):
case getType(actions.fetchTopicConfigAction.success):
case getType(actions.createTopicAction.success):
case getType(actions.fetchTopicConsumerGroupsAction.success):
case getType(actions.updateTopicAction.success):
return action.payload;
case getType(actions.fetchTopicMessagesAction.success):
Expand Down
2 changes: 2 additions & 0 deletions kafka-ui-react-app/src/redux/reducers/topics/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export const getTopicMessages = (state: RootState) =>
topicsState(state).messages;
export const getTopicListTotalPages = (state: RootState) =>
topicsState(state).totalPages;
export const getTopicConsumerGroups = (state: RootState) =>
topicsState(state).consumerGroups;

const getTopicListFetchingStatus = createFetchingSelector('GET_TOPICS');
const getTopicDetailsFetchingStatus = createFetchingSelector(
Expand Down