Skip to content

Commit 128c0d2

Browse files
authored
Topic settings editing (#55)
* Topic editing * Remove old code * Implement unique field name select * Final changes to topic editing * Cleanup eslint.json
1 parent 5a0b23e commit 128c0d2

39 files changed

+5876
-3545
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,6 @@ build/
2929

3030
### VS Code ###
3131
.vscode/
32-
/kafka-ui-api/app/node
32+
/kafka-ui-api/app/node
33+
34+
.DS_Store

kafka-ui-react-app/package-lock.json

Lines changed: 4810 additions & 3000 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

kafka-ui-react-app/package.json

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,21 @@
33
"version": "0.1.0",
44
"private": true,
55
"dependencies": {
6-
"@testing-library/jest-dom": "^4.2.4",
7-
"@testing-library/react": "^9.3.2",
8-
"@testing-library/user-event": "^7.1.2",
9-
"@types/classnames": "^2.2.9",
10-
"@types/jest": "^24.0.25",
11-
"@types/lodash": "^4.14.149",
12-
"@types/node": "^12.12.24",
13-
"@types/react": "^16.9.17",
14-
"@types/react-dom": "^16.9.0",
15-
"@types/react-redux": "^7.1.5",
16-
"@types/react-router-dom": "^5.1.3",
17-
"@types/redux": "^3.6.0",
18-
"@types/redux-thunk": "^2.1.0",
196
"bulma": "^0.8.0",
207
"bulma-switch": "^2.0.0",
218
"classnames": "^2.2.6",
22-
"json-server": "^0.15.1",
9+
"immer": "^6.0.5",
2310
"lodash": "^4.17.15",
24-
"node-sass": "^4.13.1",
2511
"pretty-ms": "^6.0.1",
2612
"react": "^16.12.0",
2713
"react-dom": "^16.12.0",
2814
"react-hook-form": "^4.5.5",
2915
"react-redux": "^7.1.3",
3016
"react-router-dom": "^5.1.2",
31-
"react-scripts": "3.3.0",
3217
"redux": "^4.0.5",
3318
"redux-thunk": "^2.3.0",
3419
"reselect": "^4.0.0",
35-
"typesafe-actions": "^5.1.0",
36-
"typescript": "~3.7.4"
20+
"typesafe-actions": "^5.1.0"
3721
},
3822
"lint-staged": {
3923
"*.{js,ts,jsx,tsx}": [
@@ -70,6 +54,19 @@
7054
]
7155
},
7256
"devDependencies": {
57+
"@testing-library/jest-dom": "^4.2.4",
58+
"@testing-library/react": "^9.3.2",
59+
"@testing-library/user-event": "^7.1.2",
60+
"@types/classnames": "^2.2.9",
61+
"@types/jest": "^24.0.25",
62+
"@types/lodash": "^4.14.149",
63+
"@types/node": "^12.12.24",
64+
"@types/react": "^16.9.17",
65+
"@types/react-dom": "^16.9.0",
66+
"@types/react-redux": "^7.1.5",
67+
"@types/react-router-dom": "^5.1.3",
68+
"@types/redux": "^3.6.0",
69+
"@types/redux-thunk": "^2.1.0",
7370
"@typescript-eslint/eslint-plugin": "^2.27.0",
7471
"@typescript-eslint/parser": "^2.27.0",
7572
"eslint": "^6.8.0",
@@ -82,7 +79,12 @@
8279
"eslint-plugin-react-hooks": "^2.5.1",
8380
"esprint": "^0.6.0",
8481
"husky": "^4.2.5",
82+
"json-server": "^0.15.1",
8583
"lint-staged": ">=10",
86-
"prettier": "^2.0.4"
87-
}
84+
"node-sass": "^4.13.1",
85+
"prettier": "^2.0.4",
86+
"react-scripts": "3.4.0",
87+
"typescript": "~3.7.4"
88+
},
89+
"proxy": "http://localhost:8080"
8890
}

kafka-ui-react-app/src/components/Topics/Details/Details.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ import {
77
clusterTopicSettingsPath,
88
clusterTopicPath,
99
clusterTopicMessagesPath,
10+
clusterTopicsTopicEditPath,
1011
} from 'lib/paths';
1112
import OverviewContainer from './Overview/OverviewContainer';
1213
import MessagesContainer from './Messages/MessagesContainer';
1314
import SettingsContainer from './Settings/SettingsContainer';
15+
import SettingsEditButton from './Settings/SettingsEditButton';
1416

1517
interface Props extends Topic, TopicDetails {
1618
clusterName: ClusterName;
@@ -30,6 +32,9 @@ const Details: React.FC<Props> = ({ clusterName, topicName }) => {
3032
{topicName}
3133
</Breadcrumb>
3234
</div>
35+
<SettingsEditButton
36+
to={clusterTopicsTopicEditPath(clusterName, topicName)}
37+
/>
3338
</div>
3439

3540
<div className="box">
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import React from 'react';
2+
import { Link } from 'react-router-dom';
3+
4+
interface Props {
5+
to: string;
6+
}
7+
8+
const SettingsEditButton: React.FC<Props> = ({ to }) => (
9+
<Link to={to}>
10+
<button type="button" className="button is-small is-warning">
11+
Edit settings
12+
</button>
13+
</Link>
14+
);
15+
16+
export default SettingsEditButton;
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import React from 'react';
2+
import {
3+
ClusterName,
4+
TopicFormData,
5+
TopicName,
6+
TopicConfigByName,
7+
TopicWithDetailedInfo,
8+
CleanupPolicy,
9+
} from 'redux/interfaces';
10+
import { useForm, FormContext } from 'react-hook-form';
11+
import { camelCase } from 'lodash';
12+
13+
import TopicForm from '../shared/Form/TopicForm';
14+
import FormBreadcrumbs from '../shared/Form/FormBreadcrumbs';
15+
16+
interface Props {
17+
clusterName: ClusterName;
18+
topicName: TopicName;
19+
topic?: TopicWithDetailedInfo;
20+
isFetched: boolean;
21+
isTopicDetailsFetched: boolean;
22+
isTopicUpdated: boolean;
23+
fetchTopicDetails: (clusterName: ClusterName, topicName: TopicName) => void;
24+
fetchTopicConfig: (clusterName: ClusterName, topicName: TopicName) => void;
25+
updateTopic: (clusterName: ClusterName, form: TopicFormData) => void;
26+
redirectToTopicPath: (clusterName: ClusterName, topicName: TopicName) => void;
27+
resetUploadedState: () => void;
28+
}
29+
30+
const DEFAULTS = {
31+
partitions: 1,
32+
replicationFactor: 1,
33+
minInSyncReplicas: 1,
34+
cleanupPolicy: CleanupPolicy.Delete,
35+
retentionBytes: -1,
36+
maxMessageBytes: 1000012,
37+
};
38+
39+
const topicParams = (topic: TopicWithDetailedInfo | undefined) => {
40+
if (!topic) {
41+
return DEFAULTS;
42+
}
43+
44+
const { name, replicationFactor } = topic;
45+
46+
const configs = topic.config?.reduce(
47+
(result: { [name: string]: string }, param) => {
48+
result[camelCase(param.name)] = param.value || param.defaultValue;
49+
return result;
50+
},
51+
{}
52+
);
53+
54+
return {
55+
...DEFAULTS,
56+
name,
57+
partitions: topic.partitionCount || DEFAULTS.partitions,
58+
replicationFactor,
59+
...configs,
60+
};
61+
};
62+
63+
let formInit = false;
64+
65+
const Edit: React.FC<Props> = ({
66+
clusterName,
67+
topicName,
68+
topic,
69+
isFetched,
70+
isTopicDetailsFetched,
71+
isTopicUpdated,
72+
fetchTopicDetails,
73+
fetchTopicConfig,
74+
updateTopic,
75+
redirectToTopicPath,
76+
}) => {
77+
const defaultValues = topicParams(topic);
78+
79+
const methods = useForm<TopicFormData>({ defaultValues });
80+
81+
const [isSubmitting, setIsSubmitting] = React.useState<boolean>(false);
82+
83+
React.useEffect(() => {
84+
fetchTopicConfig(clusterName, topicName);
85+
fetchTopicDetails(clusterName, topicName);
86+
}, [fetchTopicConfig, fetchTopicDetails, clusterName, topicName]);
87+
88+
React.useEffect(() => {
89+
if (isSubmitting && isTopicUpdated) {
90+
const { name } = methods.getValues();
91+
redirectToTopicPath(clusterName, name);
92+
}
93+
}, [isSubmitting, isTopicUpdated, redirectToTopicPath, clusterName, methods]);
94+
95+
if (!isFetched || !isTopicDetailsFetched || !topic || !topic.config) {
96+
return null;
97+
}
98+
99+
if (!formInit) {
100+
methods.reset(defaultValues);
101+
formInit = true;
102+
}
103+
104+
const config: TopicConfigByName = {
105+
byName: {},
106+
};
107+
108+
topic.config.forEach((param) => {
109+
config.byName[param.name] = param;
110+
});
111+
112+
const onSubmit = async (data: TopicFormData) => {
113+
setIsSubmitting(true);
114+
updateTopic(clusterName, data);
115+
};
116+
117+
return (
118+
<div className="section">
119+
<div className="level">
120+
<FormBreadcrumbs
121+
clusterName={clusterName}
122+
topicName={topicName}
123+
current="Edit Topic"
124+
/>
125+
</div>
126+
127+
<div className="box">
128+
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
129+
<FormContext {...methods}>
130+
<TopicForm
131+
topicName={topicName}
132+
config={config}
133+
isSubmitting={isSubmitting}
134+
isEditing
135+
onSubmit={methods.handleSubmit(onSubmit)}
136+
/>
137+
</FormContext>
138+
</div>
139+
</div>
140+
);
141+
};
142+
143+
export default Edit;
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { connect } from 'react-redux';
2+
import {
3+
RootState,
4+
ClusterName,
5+
TopicFormData,
6+
TopicName,
7+
Action,
8+
} from 'redux/interfaces';
9+
import { withRouter, RouteComponentProps } from 'react-router-dom';
10+
import {
11+
updateTopic,
12+
fetchTopicConfig,
13+
fetchTopicDetails,
14+
} from 'redux/actions';
15+
import {
16+
getTopicConfigFetched,
17+
getTopicUpdated,
18+
getIsTopicDetailsFetched,
19+
getFullTopic,
20+
} from 'redux/reducers/topics/selectors';
21+
import { clusterTopicPath } from 'lib/paths';
22+
import { ThunkDispatch } from 'redux-thunk';
23+
import Edit from './Edit';
24+
25+
interface RouteProps {
26+
clusterName: ClusterName;
27+
topicName: TopicName;
28+
}
29+
30+
type OwnProps = RouteComponentProps<RouteProps>;
31+
32+
const mapStateToProps = (
33+
state: RootState,
34+
{
35+
match: {
36+
params: { topicName, clusterName },
37+
},
38+
}: OwnProps
39+
) => ({
40+
clusterName,
41+
topicName,
42+
topic: getFullTopic(state, topicName),
43+
isFetched: getTopicConfigFetched(state),
44+
isTopicDetailsFetched: getIsTopicDetailsFetched(state),
45+
isTopicUpdated: getTopicUpdated(state),
46+
});
47+
48+
const mapDispatchToProps = (
49+
dispatch: ThunkDispatch<RootState, undefined, Action>,
50+
{ history }: OwnProps
51+
) => ({
52+
fetchTopicDetails: (clusterName: ClusterName, topicName: TopicName) =>
53+
dispatch(fetchTopicDetails(clusterName, topicName)),
54+
fetchTopicConfig: (clusterName: ClusterName, topicName: TopicName) =>
55+
dispatch(fetchTopicConfig(clusterName, topicName)),
56+
updateTopic: (clusterName: ClusterName, form: TopicFormData) =>
57+
dispatch(updateTopic(clusterName, form)),
58+
redirectToTopicPath: (clusterName: ClusterName, topicName: TopicName) => {
59+
history.push(clusterTopicPath(clusterName, topicName));
60+
},
61+
});
62+
63+
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Edit));

kafka-ui-react-app/src/components/Topics/New/CustomParams/CustomParamOptions.tsx

Lines changed: 0 additions & 20 deletions
This file was deleted.

0 commit comments

Comments
 (0)