Skip to content

Commit

Permalink
Merge pull request #42 from polarising-oss/AKHQ-58-Nodes-Details-Upda…
Browse files Browse the repository at this point in the history
…teConfigs-Frontend

AKHQ-58 - Nodes Details UpdateConfigs Frontend
  • Loading branch information
goncaloValentim authored Feb 28, 2020
2 parents 7c52ee1 + 683b76f commit 6501f26
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 60 deletions.
33 changes: 17 additions & 16 deletions client/src/components/Form/Form.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {Component} from 'react';
import React, { Component } from 'react';

import Joi from 'joi-browser';
import _ from 'lodash';
Expand All @@ -14,8 +14,8 @@ class Form extends Component {
};

validate = () => {
const options = {abortEarly: false};
const {error} = Joi.validate(this.state.formData, this.schema);
const options = { abortEarly: false };
const { error } = Joi.validate(this.state.formData, this.schema);
if (!error) return null;

const errors = {};
Expand All @@ -26,10 +26,10 @@ class Form extends Component {
return errors;
};

validateProperty = ({name, value}) => {
const obj = {[name]: value};
const schema = {[name]: this.schema[name]};
const {error} = Joi.validate(obj, schema);
validateProperty = ({ name, value }) => {
const obj = { [name]: value };
const schema = { [name]: this.schema[name] };
const { error } = Joi.validate(obj, schema);

return error ? error.details[0].message : null;
};
Expand All @@ -38,25 +38,25 @@ class Form extends Component {
e.preventDefault();

const errors = this.validate();
this.setState({errors: errors || {}});
this.setState({ errors: errors || {} });

if (errors) return;

this.doSubmit();
};

handleChange = ({currentTarget: input}) => {
const errors = {...this.state.errors};
handleChange = ({ currentTarget: input }) => {
const errors = { ...this.state.errors };
const errorMessage = this.validateProperty(input);
if (errorMessage) {
errors[input.name] = errorMessage;
} else {
delete errors[input.name];
}

const formData = {...this.state.formData};
const { formData } = this.state;
formData[input.name] = input.value;
this.setState({formData, errors});
this.setState({ formData, errors });
};

renderButton = (label, click, className, type) => {
Expand All @@ -74,8 +74,8 @@ class Form extends Component {
);
};

renderInput = (name, label, placeholder, type = 'text') => {
const {formData, errors} = this.state;
renderInput = (name, label, placeholder, type = 'text', onChange = this.handleChange, rest) => {
const { formData, errors } = this.state;

return (
<Input
Expand All @@ -86,12 +86,13 @@ class Form extends Component {
placeholder={placeholder}
onChange={this.handleChange}
error={errors[name]}
{...rest}
/>
);
};

renderSelect = (name, label, items) => {
const {formData, errors} = this.state;
const { formData, errors } = this.state;

return (
<Select
Expand All @@ -106,7 +107,7 @@ class Form extends Component {
};

renderRadioGroup = (name, label, options, onChange) => {
const {formData} = this.state;
const { formData } = this.state;
const items = [];

for (let option of options) {
Expand Down
40 changes: 23 additions & 17 deletions client/src/components/Form/Input/Input.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
import React from 'react';

const Input = props => {
const {name, label, placeholder, error, ...rest} = props;
const { name, label, placeholder, error, ...rest } = props;

return (
<div className="form-group row">
<label htmlFor={name} className="col-sm-2 col-form-label">{label}</label>
<div className="col-sm-10">
<input
{...rest}
name={name}
id={name}
className="form-control"
placeholder={placeholder}
/>
return (
<div className="form-group row">
{label !== '' ? (
<label htmlFor={name} className="col-sm-2 col-form-label">
{label}
</label>
) : (
<div></div>
)}
<div className="col-sm-10">
<input
{...rest}
key={name}
name={name}
id={name}
className="form-control"
placeholder={placeholder}
/>

{error && <div className="alert alert-danger mt-1 p-1">{error}</div>}
</div>

</div>
);
{error && <div className="alert alert-danger mt-1 p-1">{error}</div>}
</div>
</div>
);
};

export default Input;
2 changes: 1 addition & 1 deletion client/src/components/Table/Table.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class Table extends Component {
<tr key={`tableRow${index}`}>
{Object.keys(row).map(key => {
if (toPresent.find(elem => elem === key)) {
return <td>{row[key]}</td>;
return <td key={`tableColumn${key}`}>{row[key]}</td>;
}
})}
{actions && actions.length > 0 && this.renderActions(row)}
Expand Down
147 changes: 125 additions & 22 deletions client/src/containers/NodesList/Node/NodeConfigs/NodeConfigs.jsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,36 @@
import React, { Component } from 'react';
import Header from '../../../Header/Header';
import { get } from '../../../../utils/api';
import { uriNodesConfigs } from '../../../../utils/endpoints';
import { get, post } from '../../../../utils/api';
import { uriNodesConfigs, uriNodesUpdateConfigs } from '../../../../utils/endpoints';
import Table from '../../../../components/Table';
import Form from '../../../../components/Form/Form';
import converters from '../../../../utils/converters';
import _ from 'lodash';
import Joi from 'joi-browser';

class NodeConfigs extends Component {
class NodeConfigs extends Form {
state = {
host: '',
port: '',
data: [],
selectedCluster: this.props.clusterId,
selectedNode: this.props.nodeId
selectedNode: this.props.nodeId,
formData: {},
changedConfigs: {},
errors: {},
configs: []
};

schema = {};

componentDidMount() {
this.getNodesConfig();
}

async getNodesConfig() {
let configs = [];
const { selectedCluster, selectedNode } = this.state;

try {
configs = await get(uriNodesConfigs(selectedCluster, selectedNode));
this.handleData(configs.data);
Expand All @@ -31,14 +40,24 @@ class NodeConfigs extends Component {
}

handleData(configs) {
configs.map(config => {
this.createValidationSchema(config);
});

let tableNodes = configs.map(config => {
return {
id: config.name,
nameAndDescription: this.handleNameAndDescription(config.name, config.description),
value: this.getInput(config.value, config.name, config.readOnly, config.dataType),
value: this.getInput(
this.state.formData[config.name],
config.name,
config.readOnly,
config.dataType
),
typeAndSensitive: this.handleTypeAndSensitive(config.type, config.sensitive)
};
});
this.setState({ data: tableNodes });
this.setState({ data: tableNodes, configs });
}

handleDataType(dataType, value) {
Expand All @@ -54,17 +73,94 @@ class NodeConfigs extends Component {
}
}

createValidationSchema(config) {
let { formData } = this.state;
let validation;
if (isNaN(config.value)) {
validation = Joi.any();
} else {
validation = Joi.any();
}
this.schema[config.name] = validation;

formData[config.name] = isNaN(+config.value) ? config.value : +config.value;
this.setState({ formData });
}

onChange({ currentTarget: input }) {
let { data, configs } = this.state;
let config = {};
let newData = data.map(row => {
if (row.id === input.name) {
config = configs.find(config => config.name === input.name);
let { formData, changedConfigs } = this.state;
formData[input.name] = input.value;
if (input.value === config.value) {
delete changedConfigs[input.name];
} else {
changedConfigs[input.name] = input.value;
}

this.setState({ formData, changedConfigs });
return {
id: config.name,
nameAndDescription: this.handleNameAndDescription(config.name, config.description),
value: this.getInput(
this.state.formData[config.name],
config.name,
config.readOnly,
config.dataType
),
typeAndSensitive: this.handleTypeAndSensitive(config.type, config.sensitive)
};
}
return row;
});

this.setState({ data: newData });
}

async doSubmit() {
const { selectedCluster, selectedNode, changedConfigs } = this.state;
try {
await post(uriNodesUpdateConfigs(), {
clusterId: selectedCluster,
nodeId: selectedNode,
configs: changedConfigs
});

this.setState({ state: this.state });
} catch (err) {
console.error('Error:', err);
}
}

getInput(value, name, readOnly, dataType) {
return (
<div>
<input
type="text"
onChange={console.log('done')}
className="form-control"
autoComplete="off"
value={value}
readOnly={readOnly}
/>
{dataType === 'TEXT' ? (
<input
type="text"
onChange={value => this.onChange(value)}
className="form-control"
autoComplete="off"
value={value}
readOnly={readOnly}
name={name}
placeholder="Default"
/>
) : (
<input
type="number"
onChange={value => this.onChange(value)}
className="form-control"
autoComplete="off"
value={value}
readOnly={readOnly}
name={name}
placeholder="Default"
/>
)}
{this.handleDataType(dataType, value)}
</div>
);
Expand Down Expand Up @@ -113,13 +209,20 @@ class NodeConfigs extends Component {
render() {
const { data, selectedNode, selectedCluster } = this.state;
return (
<div>
<Table
colNames={['Name', 'Value', 'Type']}
toPresent={['nameAndDescription', 'value', 'typeAndSensitive']}
data={data}
/>
</div>
<form
encType="multipart/form-data"
className="khq-form mb-0"
onSubmit={() => this.handleSubmit()}
>
<div>
<Table
colNames={['Name', 'Value', 'Type']}
toPresent={['nameAndDescription', 'value', 'typeAndSensitive']}
data={data}
/>
{this.renderButton('Update configs', this.handleSubmit, undefined, 'submit')}
</div>
</form>
);
}
}
Expand Down
4 changes: 4 additions & 0 deletions client/src/utils/endpoints.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export const uriNodesConfigs = (clusterId, nodeId) => {
);
};

export const uriNodesUpdateConfigs = () => {
return `${apiUrl}/cluster/nodes/update-configs`;
};

export const uriNodesLogs = (clusterId, nodeId) => {
return (
`${apiUrl}/cluster/nodes/logs${clusterId ? '?clusterId=' + clusterId : ''}` +
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/kafkahq/service/NodeService.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ public List<LogDTO> getLogDTOList(String clusterId, Integer nodeId) throws Execu
}

public List<ConfigDTO> updateConfigs(ConfigOperationDTO configOperation) throws Throwable {
List<Config> updated = ConfigRepository.updatedConfigs(configOperation.getConfigs(), this.configRepository.findByBroker(configOperation.getClusterId(), configOperation.getNodeId()));
Map<String, String> configs = nodeMapper.convertConfigsMap(configOperation.getConfigs());
List<Config> updated = ConfigRepository.updatedConfigs(configs, this.configRepository.findByBroker(configOperation.getClusterId(), configOperation.getNodeId()));

if (updated.size() == 0) {
throw new IllegalArgumentException("No config to update");
Expand Down
Loading

0 comments on commit 6501f26

Please sign in to comment.