From 6241bd087d1fb3a6d793b88d242cf7e85631e15c Mon Sep 17 00:00:00 2001 From: Paolo Haider <51787541+xpr3sso@users.noreply.github.com> Date: Thu, 16 Sep 2021 22:13:36 +0200 Subject: [PATCH] feat(ui): add multi-message produce feature (#791) Co-authored-by: xpr3sso --- client/src/components/Form/Form.jsx | 21 +++- client/src/components/Form/styles.scss | 16 ++- .../Topic/TopicProduce/TopicProduce.jsx | 98 +++++++++++++++---- .../org/akhq/controllers/TopicController.java | 28 +++--- src/main/java/org/akhq/models/KeyValue.java | 14 +++ .../akhq/repositories/RecordRepository.java | 48 +++++++++ .../akhq/controllers/TopicControllerTest.java | 53 +++++++--- 7 files changed, 231 insertions(+), 47 deletions(-) create mode 100644 src/main/java/org/akhq/models/KeyValue.java diff --git a/client/src/components/Form/Form.jsx b/client/src/components/Form/Form.jsx index 7898a5301..e6909fdad 100644 --- a/client/src/components/Form/Form.jsx +++ b/client/src/components/Form/Form.jsx @@ -112,8 +112,9 @@ class Form extends Root { ); }; - renderJSONInput = (name, label, onChange) => { + renderJSONInput = (name, label, onChange, textMode, options) => { const { formData, errors } = this.state; + const inputMode = textMode ? "text" : (formData.schemaType === "PROTOBUF" ? "protobuf" : "json") return (
{label !== '' ? ( @@ -125,7 +126,7 @@ class Form extends Root { )}
{errors[name] &&
{errors[name]}
} @@ -238,6 +240,21 @@ class Form extends Root { ); }; + + renderCheckbox = (name, label, isChecked, onChange, isDefaultChecked) => { + return ( + + ); + }; } export default Form; + diff --git a/client/src/components/Form/styles.scss b/client/src/components/Form/styles.scss index 8e0808a46..c7c7b290e 100644 --- a/client/src/components/Form/styles.scss +++ b/client/src/components/Form/styles.scss @@ -24,4 +24,18 @@ input.placeholder{ border-bottom-color: transparent !important; width: 100%; padding: 0px; -} \ No newline at end of file +} + +.ace_active-line { + opacity: 0.2; +} + +.ace_placeholder { + font-family: "Source Code Pro",SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace; + transform: scale(1); +} + +.form-input-check{ + margin-block: auto; +} + diff --git a/client/src/containers/Topic/TopicProduce/TopicProduce.jsx b/client/src/containers/Topic/TopicProduce/TopicProduce.jsx index 83a0aaf0e..bf79f2492 100644 --- a/client/src/containers/Topic/TopicProduce/TopicProduce.jsx +++ b/client/src/containers/Topic/TopicProduce/TopicProduce.jsx @@ -24,7 +24,8 @@ class TopicProduce extends Form { key: '', hKey0: '', hValue0: '', - value: '' + value: '', + keyValueSeparator: ':' }, datetime: new Date(), openDateModal: false, @@ -40,7 +41,9 @@ class TopicProduce extends Form { valueSchemaSearchValue: '', selectedValueSchema: '', clusterId: '', - topicId: '' + topicId: '', + multiMessage: false, + valuePlaceholder: '{"param": "value"}' }; schema = { @@ -58,7 +61,10 @@ class TopicProduce extends Form { .label('hValue0'), value: Joi.string() .allow('') - .label('Value') + .label('Value'), + keyValueSeparator: Joi.string() + .min(1) + .label('keyValueSeparator') }; async componentDidMount() { @@ -98,7 +104,8 @@ class TopicProduce extends Form { selectedKeySchema, selectedValueSchema, keySchema, - valueSchema + valueSchema, + multiMessage } = this.state; const { clusterId, topicId } = this.props.match.params; @@ -110,9 +117,11 @@ class TopicProduce extends Form { partition: formData.partition, key: formData.key, timestamp: datetime.toISOString(), - value: JSON.parse(JSON.stringify(formData.value)), + value: multiMessage ? formData.value : JSON.parse(JSON.stringify(formData.value)), keySchema: schemaKeyToSend ? schemaKeyToSend.id : '', - valueSchema: schemaValueToSend ? schemaValueToSend.id : '' + valueSchema: schemaValueToSend ? schemaValueToSend.id : '', + multiMessage: multiMessage, + keyValueSeparator: formData.keyValueSeparator }; let headers = {}; @@ -134,6 +143,55 @@ class TopicProduce extends Form { }); } + renderMultiMessage() { + const { formData, multiMessage } = this.state; + + return ( +
+ +
+ {this.renderCheckbox( + 'isMultiMessage', + '', + multiMessage, + () => { + this.setState({multiMessage: !multiMessage, + valuePlaceholder: this.getPlaceholderValue(!multiMessage, formData.keyValueSeparator)}) + }, + false + )} + + + { + this.setState({ + formData: { ...formData, + keyValueSeparator: event.target.value}, + valuePlaceholder: this.getPlaceholderValue(!multiMessage, event.target.value)}) + } + } + /> +
+
+ ); + } + + getPlaceholderValue(isMultiMessage, keyValueSeparator) { + if(isMultiMessage) { + return "key1" + keyValueSeparator + "{\"param\": \"value1\"}\n" + + "key2" + keyValueSeparator + "{\"param\": \"value2\"}"; + } else { + return '{"param": "value"}'; + } + } + renderHeaders() { let headers = []; @@ -284,13 +342,13 @@ class TopicProduce extends Form { selectedKeySchema, valueSchema, valueSchemaSearchValue, - selectedValueSchema + selectedValueSchema, + multiMessage } = this.state; let date = moment(datetime); return (
-
{this.renderSelect('partition', 'Partition', partitions, value => { this.setState({ formData: { ...formData, partition: value.target.value } }); @@ -313,11 +371,11 @@ class TopicProduce extends Form { 'key' ) )} - {this.renderInput('key', 'Key', 'Key', 'Key')} -
-
+ + {(this.renderInput('key', 'Key', 'Key', 'Key', undefined, undefined, undefined, undefined,{ disabled: multiMessage }))} {this.renderHeaders()} + {this.renderDropdown( 'Value schema', valueSchema.map(value => value.subject), @@ -336,14 +394,19 @@ class TopicProduce extends Form { 'value' ) )} + + {this.renderMultiMessage()} + {this.renderJSONInput('value', 'Value', value => { this.setState({ - formData: { - ...formData, - value: value - } - }); - })} + formData: { + ...formData, + value: value + } + })}, + multiMessage, // true -> 'text' mode; json, protobuff, ... mode otherwise + { placeholder: this.getPlaceholderValue(multiMessage, formData.keyValueSeparator) } + )}