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: add ui options endpoint #511

Merged
merged 5 commits into from
Nov 20, 2020
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
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,23 +270,30 @@ akhq:
* `akhq.pagination.page-size` number of topics per page (default : 25)

#### Topic List
* `akhq.topic.default-view` is default list view (ALL, HIDE_INTERNAL, HIDE_INTERNAL_STREAM, HIDE_STREAM)
* `akhq.topic.internal-regexps` is list of regexp to be considered as internal (internal topic can't be deleted or updated)
* `akhq.topic.stream-regexps` is list of regexp to be considered as internal stream topic

#### Topic creation default values

These parameters are the default values used in the topic creation page.

* `akhq.topic.retention` Default retention in ms
* `akhq.topic.replication` Default number of replica to use
* `akhq.topic.partition` Default number of partition

#### Topic Data
* `akhq.topic-data.sort`: default sort order (OLDEST, NEWEST) (default: OLDEST)
* `akhq.topic-data.size`: max record per page (default: 50)
* `akhq.topic-data.poll-timeout`: The time, in milliseconds, spent waiting in poll if data is not available in the
buffer (default: 1000).
* `akhq.topic-data.poll-timeout`: The time, in milliseconds, spent waiting in poll if data is not available in the buffer (default: 1000).

#### Ui Settings
##### Topics
* `akhq.ui-options.topic.default-view` is default list view (ALL, HIDE_INTERNAL, HIDE_INTERNAL_STREAM, HIDE_STREAM) (default: HIDE_INTERNAL)
* `akhq.ui-options.topic.skip-consumer-groups` hide consumer groups columns on topic list
* `akhq.ui-options.topic.skip-last-record` hide the last records on topic list

##### Topic Data
* `akhq.ui-options.topic-data.sort`: default sort order (OLDEST, NEWEST) (default: OLDEST)


#### Protobuf deserialization

Expand Down
21 changes: 19 additions & 2 deletions application.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ akhq:
descriptor-file-base64: "Cs4LChhzdHJlYW1pbmctcHJvdG9jb2wucHJvdG8SLmNvbS5uZXRjcmFja2VyLnByb3RvYnVmLnNlcmlhbGl6YXRpb24ucHJvdG9jb2wiIwoFUG9pbnQSDAoBeBgBIAEoAVIBeBIMCgF5GAIgASgBUgF5IscFCgVEYXR1bRIfCgtjb2x1bW5fbmFtZRgBIAEoCVIKY29sdW1uTmFtZRJbCgtjb2x1bW5fdHlwZRgCIAEoDjI6LmNvbS5uZXRjcmFja2VyLnByb3RvYnVmLnNlcmlhbGl6YXRpb24ucHJvdG9jb2wuQ29sdW1uVHlwZVIKY29sdW1uVHlwZRIfCgtzY2hlbWFfbmFtZRgLIAEoCVIKc2NoZW1hTmFtZRJ4ChFzY2hlbWFfcGFyYW1ldGVycxgMIAMoCzJLLmNvbS5uZXRjcmFja2VyLnByb3RvYnVmLnNlcmlhbGl6YXRpb24ucHJvdG9jb2wuRGF0dW0uU2NoZW1hUGFyYW1ldGVyc0VudHJ5UhBzY2hlbWFQYXJhbWV0ZXJzEiUKDWRhdHVtX2ludGVnZXIYAyABKAVIAFIMZGF0dW1JbnRlZ2VyEh8KCmRhdHVtX2xvbmcYBCABKANIAFIJZGF0dW1Mb25nEiEKC2RhdHVtX2Zsb2F0GAUgASgCSABSCmRhdHVtRmxvYXQSIwoMZGF0dW1fZG91YmxlGAYgASgBSABSC2RhdHVtRG91YmxlEiUKDWRhdHVtX2Jvb2xlYW4YByABKAhIAFIMZGF0dW1Cb29sZWFuEiMKDGRhdHVtX3N0cmluZxgIIAEoCUgAUgtkYXR1bVN0cmluZxIhCgtkYXR1bV9ieXRlcxgJIAEoDEgAUgpkYXR1bUJ5dGVzElgKC2RhdHVtX3BvaW50GAogASgLMjUuY29tLm5ldGNyYWNrZXIucHJvdG9idWYuc2VyaWFsaXphdGlvbi5wcm90b2NvbC5Qb2ludEgAUgpkYXR1bVBvaW50GkMKFVNjaGVtYVBhcmFtZXRlcnNFbnRyeRIQCgNrZXkYASABKAlSA2tleRIUCgV2YWx1ZRgCIAEoCVIFdmFsdWU6AjgBQgcKBWRhdHVtIlIKA1JvdxJLCgVkYXR1bRgBIAMoCzI1LmNvbS5uZXRjcmFja2VyLnByb3RvYnVmLnNlcmlhbGl6YXRpb24ucHJvdG9jb2wuRGF0dW1SBWRhdHVtImkKBlNvdXJjZRIcCgljb25uZWN0b3IYASABKAlSCWNvbm5lY3RvchIcCgl0aW1lc3RhbXAYAiABKARSCXRpbWVzdGFtcBIjCg1sYXN0X3NuYXBzaG90GAMgASgIUgxsYXN0U25hcHNob3QijwIKCEVudmVsb3BlEhwKCXRpbWVzdGFtcBgBIAEoBFIJdGltZXN0YW1wEhwKCW9wZXJhdGlvbhgCIAEoCVIJb3BlcmF0aW9uElAKCGRhdGFfcm93GAMgASgLMjMuY29tLm5ldGNyYWNrZXIucHJvdG9idWYuc2VyaWFsaXphdGlvbi5wcm90b2NvbC5Sb3dIAFIHZGF0YVJvdxIdCglkYXRhX2pzb24YBCABKAlIAFIIZGF0YUpzb24STgoGc291cmNlGAUgASgLMjYuY29tLm5ldGNyYWNrZXIucHJvdG9idWYuc2VyaWFsaXphdGlvbi5wcm90b2NvbC5Tb3VyY2VSBnNvdXJjZUIGCgRkYXRhKnMKCkNvbHVtblR5cGUSCwoHSU5URUdFUhAAEggKBExPTkcQARIJCgVGTE9BVBACEgoKBkRPVUJMRRADEgsKB0JPT0xFQU4QBBIKCgZTVFJJTkcQBRIICgRKU09OEAYSCQoFQllURVMQBxIJCgVQT0lOVBAIQkUKLmNvbS5uZXRjcmFja2VyLnByb3RvYnVmLnNlcmlhbGl6YXRpb24ucHJvdG9jb2xCEVN0cmVhbWluZ1Byb3RvY29sSAFiBnByb3RvMw=="
key-message-type: "Row"
value-message-type: "Envelope"
# Ui Cluster Options (optional)
ui-options:
topic:
default-view: ALL # default list view (ALL, HIDE_INTERNAL, HIDE_INTERNAL_STREAM, HIDE_STREAM). Overrides default
skip-consumer-groups: false # Skip loading consumer group information when showing topics. Overrides default
skip-last-record: true # Skip loading last record date information when showing topics. Overrides default
topic-data:
sort: NEWEST # default sort order (OLDEST, NEWEST) (default: OLDEST). Overrides default

my-cluster-ssl:
properties:
Expand Down Expand Up @@ -125,7 +133,6 @@ akhq:
retention: 172800000 # default retention period when creating topic
partition: 3 # default number of partition when creating topic
replication: 3 # default number of replicas when creating topic
default-view: HIDE_INTERNAL # default list view (ALL, HIDE_INTERNAL, HIDE_INTERNAL_STREAM, HIDE_STREAM)
internal-regexps: # list of regexp to be considered as internal (internal topic can't be deleted or updated)
- "^_.*$"
- "^.*_schemas$"
Expand All @@ -136,13 +143,23 @@ akhq:
- "^.*-changelog$"
- "^.*-repartition$"
- "^.*-rekey$"
skip-consumer-groups: false # Skip loading consumer group information when showing topics
skip-last-record: false # Skip loading last record date information when showing topics

# Topic display data options (optional)
topic-data:
sort: OLDEST # default sort order (OLDEST, NEWEST) (default: OLDEST)
size: 50 # max record per page (default: 50)
poll-timeout: 1000 # The time, in milliseconds, spent waiting in poll if data is not available in the buffer.

# Ui Global Options (optional)
ui-options:
topic:
default-view: ALL # default list view (ALL, HIDE_INTERNAL, HIDE_INTERNAL_STREAM, HIDE_STREAM). Overrides default
skip-consumer-groups: false # Skip loading consumer group information when showing topics. Overrides default
skip-last-record: true # Skip loading last record date information when showing topics. Overrides default
topic-data:
sort: NEWEST # default sort order (OLDEST, NEWEST) (default: OLDEST). Overrides default

# Auth & Roles (optional)
security:
default-group: admin # Default groups for all the user even unlogged user
Expand Down
8 changes: 8 additions & 0 deletions client/src/components/Root/Root.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,19 @@ class Root extends Component {
cancel = axios.CancelToken.source();

componentWillUnmount() {
this.cancelAxiosRequests();
}

cancelAxiosRequests() {
if (this.cancel !== undefined) {
this.cancel.cancel('cancel all');
}
}

renewCancelToken() {
this.cancel = axios.CancelToken.source();
}

getApi(url) {
return get(url, {cancelToken: this.cancel.token})
}
Expand Down
146 changes: 146 additions & 0 deletions client/src/containers/Settings/Settings.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import React from 'react';
import Joi from 'joi-browser';
import Form from '../../components/Form/Form';
import Header from '../Header';
import {getUIOptions, setUIOptions} from "../../utils/localstorage";
import './styles.scss';
import {toast} from "react-toastify";

class Setttings extends Form {
state = {
clusterId: '',
formData: {
topicDefaultView: '',
topicDataSort: '',
skipConsumerGroups: false,
skipLastRecord: false
},
errors: {}
};

topicDefaultView = [ { _id: 'ALL', name: 'ALL' }, { _id: 'HIDE_INTERNAL', name: 'HIDE_INTERNAL' },
{ _id: 'HIDE_INTERNAL_STREAM', name: 'HIDE_INTERNAL_STREAM' }, { _id: 'HIDE_STREAM', name: 'HIDE_STREAM' } ];
topicDataSort = [ { _id: 'OLDEST', name: 'OLDEST' }, { _id: 'NEWEST', name: 'NEWEST' } ];

schema = {
topicDefaultView: Joi.string().required(),
topicDataSort: Joi.string().required(),
skipConsumerGroups: Joi.boolean().optional(),
skipLastRecord: Joi.boolean().optional()
};

componentDidMount() {
const { clusterId } = this.props.match.params;

const uiOptions = getUIOptions(clusterId);

this.setState({ clusterId, formData: {
topicDefaultView: (uiOptions && uiOptions.topic)? uiOptions.topic.defaultView : '',
topicDataSort: (uiOptions && uiOptions.topicData)? uiOptions.topicData.sort : '',
skipConsumerGroups: (uiOptions && uiOptions.topic)? uiOptions.topic.skipConsumerGroups : false,
skipLastRecord: (uiOptions && uiOptions.topic)? uiOptions.topic.skipLastRecord : false
}})
}

checkedSkipConsumerGroups = (event) => {
const { formData } = this.state;
formData.skipConsumerGroups = event.target.checked;
this.setState({formData});
}



doSubmit() {
const { clusterId, formData } = this.state;
setUIOptions(clusterId,
{ topic: { defaultView: formData.topicDefaultView,
skipConsumerGroups: formData.skipConsumerGroups,
skipLastRecord: formData.skipLastRecord },
topicData: {sort: formData.topicDataSort} });
toast.success(`Settings for cluster '${clusterId}' updated successfully.`);
}

render() {
return (
<div>
<form
encType="multipart/form-data"
className="khq-form khq-form-config"
onSubmit={() => this.doSubmit()}
>
<Header title="Settings" history={this.props.history} />
<fieldset id="topic" key="topic">
<legend id="topic">Topic</legend>
{this.renderSelect(
'topicDefaultView',
'Default View',
this.topicDefaultView,
({ currentTarget: input }) => {
const { formData } = this.state;
formData.topicDefaultView = input.value;
this.setState({formData});
},
'col-sm-10',
'select-wrapper settings-wrapper',
true,
{ className: 'form-control' }
)}
<div className="select-wrapper settings-wrapper row">
<span className="col-sm-2 col-form-label">Skip Consumer Groups</span>
<span className="col-sm-10 row-checkbox-settings">
<input
type="checkbox"
value="skipConsumerGroups"
checked={this.state.formData.skipConsumerGroups || false}
onChange={this.checkedSkipConsumerGroups}
/></span>
</div>
<div className="select-wrapper settings-wrapper row">
<span className="col-sm-2 col-form-label">Skip Last Record Date</span>
<span className="col-sm-10 row-checkbox-settings">
<input
type="checkbox"
value="skipLastRecord"
checked={this.state.formData.skipLastRecord || false}
onChange={ event => {
const { formData } = this.state;
formData.skipLastRecord = event.target.checked;
this.setState({formData});
}}
/></span>
</div>
</fieldset>

<fieldset id="topicData" key="topicData">
<legend id="topicData">Topic Data</legend>
{this.renderSelect(
'topicDataSort',
'Sort',
this.topicDataSort,
({ currentTarget: input }) => {
const { formData } = this.state;
formData.topicDataSort = input.value;
this.setState({formData});
},
'col-sm-10',
'select-wrapper settings-wrapper',
true,
{ className: 'form-control' }
)}
</fieldset>

{this.renderButton(
'Update Settings',
() => {
this.doSubmit();
},
undefined,
'button'
)}
</form>
</div>
);
}
}

export default Setttings;
3 changes: 3 additions & 0 deletions client/src/containers/Settings/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Settings from './Settings';

export default Settings;
7 changes: 7 additions & 0 deletions client/src/containers/Settings/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.settings-wrapper {
margin-bottom: 1rem;
}

.row-checkbox-settings {
align-self: center
}
2 changes: 2 additions & 0 deletions client/src/containers/SideBar/Sidebar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ class Sidebar extends Component {
};

changeSelectedCluster(newSelectedCluster) {

this.setState(
{
selectedCluster: newSelectedCluster.id,
Expand Down Expand Up @@ -324,6 +325,7 @@ class Sidebar extends Component {
{listConnects}
</NavItem>
)}
{this.renderMenuItem('fa fa-fw fa-gear', constants.SETTINGS, 'Settings')}
</SideNav.Nav>
</SideNav>
);
Expand Down
8 changes: 6 additions & 2 deletions client/src/containers/Topic/Topic/TopicData/TopicData.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import Root from '../../../../components/Root';
import { basePath } from '../../../../utils/endpoints';
import {getClusterUIOptions} from "../../../../utils/functions";

class TopicData extends Root {
state = {
Expand Down Expand Up @@ -65,15 +66,18 @@ class TopicData extends Root {
this.checkProps();
};

checkProps = () => {
async checkProps() {
const { clusterId, topicId } = this.props.match.params;
const query = new URLSearchParams(this.props.location.search);

const uiOptions = await getClusterUIOptions(clusterId);

this.setState(
{
selectedCluster: clusterId,
selectedTopic: topicId,
sortBy: (query.get('sort'))? query.get('sort') : this.state.sortBy,
sortBy: (query.get('sort'))? query.get('sort') : (uiOptions && uiOptions.topicData && uiOptions.topicData.sort)?
uiOptions.topicData.sort : this.state.sortBy,
partition: (query.get('partition'))? query.get('partition') : this.state.partition,
datetime: (query.get('timestamp'))? new Date(query.get('timestamp')) : this.state.datetime,
offsetsSearch: (query.get('after'))? query.get('after') : this.state.offsetsSearch,
Expand Down
Loading