This is an automated email from the ASF dual-hosted git repository.
yangwenjie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu-dashboard.git
The following commit(s) were added to refs/heads/master by this push:
new b709682a [feature] Transform the 'divide,' 'grpc,' and 'websocket'
plugins for discovery (#388)
b709682a is described below
commit b709682adc63a9af56e9311558692f96f4f22db3
Author: lulu <[email protected]>
AuthorDate: Wed Jan 3 21:25:43 2024 +0800
[feature] Transform the 'divide,' 'grpc,' and 'websocket' plugins for
discovery (#388)
* refactor: change "tcp" to "discovery"
* refactor: Change the parameters of componentDidUpdate to adapt to the new
version of react
* feature: add tabPane to divide plugin selector
* refactor: Modify variables with the same name
* refactor: Completed the addition of the discovery page in the divide
plugin
* refactor: Completed the addition of the discovery page in the divide
plugin
* feature: addSelector success
* feature: updateSelector success
* refactor: refactor updateSelector success
* fix: fix discovery upstream data acquisition
* [feature] Add discovery tabPane to Websocket and Grpc
* feature: add upstream api
* fix: selector update bug
* fix:upstream bug
* Revert "fix:upstream bug"
This reverts commit db7893c2d04e1d6b70b14e6e99ec2c300ea1ada3.
* refactor:upstream update
* fix:upstream bug
* feature:config import from backend
* fix:null config bug
* refactor: change table to editableTable
* refactor: remove unused code
* refactor: remove protocol in upstreamTable, change tcp table to editable
table
* fix: add startupTime and warmupTime
* refactor: change editableTable
* fix: fix DiscoveryImportModal
* fix: fix button
* fix: remove unused code
* fix: discovery props set
* refactor: tcp editableTable
* fix: add eureka discovery icon
* fix: remove unused local variable
* fix: remove unused code
* fix: fix discovery props style bug and add i18n fields
* fix: remove unused component
---
src/locales/en-US.json | 11 +-
src/locales/zh-CN.json | 9 +-
src/models/common.js | 3 +-
src/models/discovery.js | 41 +-
src/routes/Plugin/Common/Selector.js | 928 ++++++++++++++-------
src/routes/Plugin/Common/index.js | 189 ++++-
.../Discovery/{TcpCard.js => DiscoveryCard.js} | 11 +-
.../Plugin/Discovery/DiscoveryConfigModal.js | 18 +-
src/routes/Plugin/Discovery/DiscoveryIcon.js | 15 +
.../Plugin/Discovery/DiscoveryImportModal.js | 94 +++
.../Plugin/Discovery/DiscoveryUpstreamTable.js | 287 +++++++
src/routes/Plugin/Discovery/ProxySelectorModal.js | 75 +-
src/routes/Plugin/Discovery/UpstreamTable.js | 213 -----
.../Plugin/Discovery/{tcp.less => discovery.less} | 2 +-
src/routes/Plugin/Discovery/index.js | 92 +-
.../PluginRuleHandle/ParamPluginRuleHandle.js | 6 +-
src/services/api.js | 19 +
src/utils/AuthRoute.js | 1 -
18 files changed, 1383 insertions(+), 631 deletions(-)
diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index bbabfe67..a48e9021 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -405,12 +405,19 @@
"SHENYU.DISCOVERY.SELECTOR.CONFIGURATION": "Discovery Configuration",
"SHENYU.DISCOVERY.SELECTOR.UPSTREAM": "Discovery Upstreams",
"SHENYU.DISCOVERY.SELECTOR.UPSTREAM.ADD": "Add Discovery Upstream",
- "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DELETE": "Sure to delete?",
+ "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DELETE.CONFIRM": "Sure to delete?",
+ "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.CANCEL.CONFIRM": "Sure to cancel?",
"SHENYU.DISCOVERY.SELECTOR.UPSTREAM.OPERATION": "operation",
+ "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.CANCEL": "Cancel",
+ "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.SAVE": "Save",
+ "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.EDIT": "Edit",
"SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DateCreated": "DateCreated",
"SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DateUpdated": "DateUpdated",
"SHENYU.DISCOVERY.SELECTOR.CONFIG.BASIC": "Basic Config",
"SHENYU.DISCOVERY.SELECTOR.CONFIG.DISCOVERY": "Discovery Config",
+ "SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT": "Import Background Discovery
Config",
+ "SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT.CONFIRM": "Configuration Import
Confirmation",
"SHENYU.DISCOVERY.SELECTOR.DELETE.CONFIRM": "Are you sure to delete this
selector?",
+ "SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT.SURE": "Import",
"SHENYU.COMMON.REFRESH": "Refresh"
-}
\ No newline at end of file
+}
diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json
index e457a42a..369f9913 100644
--- a/src/locales/zh-CN.json
+++ b/src/locales/zh-CN.json
@@ -408,12 +408,19 @@
"SHENYU.DISCOVERY.SELECTOR.CONFIGURATION": "服务发现配置",
"SHENYU.DISCOVERY.SELECTOR.UPSTREAM": "服务下游列表",
"SHENYU.DISCOVERY.SELECTOR.UPSTREAM.ADD": "添加下游服务",
- "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DELETE": "确定删除吗",
+ "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DELETE.CONFIRM": "确定删除吗",
+ "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.CANCEL.CONFIRM": "确定取消吗",
"SHENYU.DISCOVERY.SELECTOR.UPSTREAM.OPERATION": "操作",
+ "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.CANCEL": "取消",
+ "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.SAVE": "保存",
+ "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.EDIT": "编辑",
"SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DateCreated": "创建时间",
"SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DateUpdated": "更新时间",
"SHENYU.DISCOVERY.SELECTOR.CONFIG.BASIC": "基本配置",
"SHENYU.DISCOVERY.SELECTOR.CONFIG.DISCOVERY": "服务发现",
+ "SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT": "导入后台服务发现配置",
+ "SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT.CONFIRM": "后台配置导入确认",
+ "SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT.SURE": "导入",
"SHENYU.DISCOVERY.SELECTOR.DELETE.CONFIRM": "确定删除该选择器吗?",
"SHENYU.COMMON.REFRESH": "刷新"
}
diff --git a/src/models/common.js b/src/models/common.js
index 1c392de7..db1ae7f0 100644
--- a/src/models/common.js
+++ b/src/models/common.js
@@ -109,7 +109,8 @@ export default {
const json = yield call(addSelector, payload);
if (json.code === 200) {
message.success(getIntlContent('SHENYU.COMMON.RESPONSE.ADD.SUCCESS'));
- callback();
+ const selectorId = json.data;
+ callback(selectorId);
yield put({ type: "reload", fetchValue });
} else {
message.warn(json.message);
diff --git a/src/models/discovery.js b/src/models/discovery.js
index fc198069..0eed512a 100644
--- a/src/models/discovery.js
+++ b/src/models/discovery.js
@@ -25,8 +25,9 @@ import {
updateProxySelector,
getDiscovery,
refreshProxySelector,
- deleteDiscovery
-
+ deleteDiscovery,
+ bindingSelector,
+ updateDiscoveryUpstream
} from "../services/api";
import {getIntlContent} from "../utils/IntlUtils";
@@ -35,7 +36,7 @@ export default {
state: {
typeEnums: [],
- selectorList: [],
+ proxySelectorList: [],
chosenType: '',
totalPage: 0,
currentPage: 1,
@@ -43,7 +44,7 @@ export default {
},
effects: {
- * fetchSelector(params, {call, put}) {
+ * fetchProxySelectors(params, {call, put}) {
const {payload} = params;
const json = yield call(fetchProxySelector, payload);
if (json.code === 200) {
@@ -80,6 +81,7 @@ export default {
message.warn(json.message);
}
},
+
* delete(params, {call, put}) {
const {payload, fetchValue} = params;
const { list } = payload;
@@ -92,6 +94,7 @@ export default {
message.warn(json.message);
}
},
+
* update(params, {call, put}) {
const {payload, callback, fetchValue} = params;
const json = yield call(updateProxySelector, payload);
@@ -103,11 +106,12 @@ export default {
message.warn(json.message);
}
},
+
* reload(params, {put}) {
const {fetchValue} = params;
const {name = '', currentPage, pageSize} = fetchValue;
const payload = {name, currentPage, pageSize};
- yield put({type: "fetchSelector", payload});
+ yield put({type: "fetchProxySelectors", payload});
},
@@ -116,9 +120,9 @@ export default {
const json = yield call(postDiscoveryInsertOrUpdate, payload);
if (json.code === 200) {
message.success(getIntlContent('SHENYU.COMMON.RESPONSE.CONFIGURATION.SUCCESS'));
- const { data } = json;
+ const discoveryConfigData = json.data;
if (callback) {
- callback(data);
+ callback(discoveryConfigData);
}
} else {
message.warn(json.message);
@@ -162,6 +166,27 @@ export default {
}
},
+ * bindSelector(params, {call}) {
+ const {payload} = params;
+ const json = yield call(bindingSelector, payload);
+ if (json.code === 200) {
+ //
message.success(getIntlContent('SHENYU.COMMON.RESPONSE.ADD.SUCCESS'));
+ // callback();
+ } else {
+ message.warn(json.message);
+ }
+ },
+
+ * updateDiscoveryUpstream(params, {call}) {
+ const { discoveryHandlerId, upstreams } = params.payload;
+ const json = yield call(updateDiscoveryUpstream, discoveryHandlerId,
upstreams);
+ if (json.code === 200) {
+ //
message.success(getIntlContent('SHENYU.COMMON.RESPONSE.UPDATE.SUCCESS'));
+ } else {
+ message.warn(json.message);
+ }
+ },
+
},
reducers: {
@@ -169,7 +194,7 @@ export default {
return {
...state,
totalPage: payload.total,
- selectorList: payload.dataList,
+ proxySelectorList: payload.dataList,
};
},
diff --git a/src/routes/Plugin/Common/Selector.js
b/src/routes/Plugin/Common/Selector.js
index 40e33413..021d2a09 100644
--- a/src/routes/Plugin/Common/Selector.js
+++ b/src/routes/Plugin/Common/Selector.js
@@ -32,15 +32,19 @@ import {
Icon,
InputNumber,
DatePicker,
- TimePicker
+ TimePicker, Tabs, Divider
} from "antd";
import { connect } from "dva";
import classnames from "classnames";
import styles from "../index.less";
import { getIntlContent } from "../../../utils/IntlUtils";
import SelectorCopy from "./SelectorCopy";
+import { findKeyByValue } from "../../../utils/utils";
+import DiscoveryImportModal from "../Discovery/DiscoveryImportModal";
+import EditableFormTable from "../Discovery/DiscoveryUpstreamTable.js";
const { Item } = Form;
+const { TabPane } = Tabs;
const { Option } = Select;
const formItemLayout = {
@@ -54,10 +58,11 @@ const formCheckLayout = {
let id = 0;
-@connect(({ pluginHandle, global, shenyuDict }) => ({
+@connect(({ pluginHandle, global, shenyuDict, discovery }) => ({
pluginHandle,
platform: global.platform,
- shenyuDict
+ shenyuDict,
+ discovery
}))
class AddModal extends Component {
constructor(props) {
@@ -75,6 +80,7 @@ class AddModal extends Component {
}
const { divideUpstreams = [], gray = false, serviceId = "" } = data;
+ const { discoveryUpstreams = [], isDiscovery, isAdd = true,
discoveryConfig = {} } = this.props;
if (pluginId === "8") {
id = divideUpstreams.length;
@@ -85,18 +91,36 @@ class AddModal extends Component {
gray,
serviceId,
divideUpstreams,
-
- visible: false
+ visible: false,
+ pluginHandleList: [],
+ upstreams: discoveryUpstreams,
+ recordCount: discoveryUpstreams ? discoveryUpstreams.length : 0,
+ discoveryHandler: null,
+ defaultValueList: null,
+ configPropsJson: {},
+ selectedDiscoveryValue : 'local',
+ showDiscoveryImportModal: false,
+ importedDiscoveryId: ''
};
this.initSelectorCondition(props);
- this.initDics();
+
+ if (isDiscovery && !isAdd){
+ this.state.configPropsJson = JSON.parse(discoveryConfig.props);
+ this.state.selectedDiscoveryValue = discoveryConfig.discoveryType;
+ }
}
componentDidMount() {
- const { dispatch, pluginId, handle, multiSelectorHandle } = this.props;
+ const { dispatch, pluginId, handle, multiSelectorHandle, isDiscovery } =
this.props;
this.setState({ pluginHandleList: [] });
let type = 1;
+ this.initDics();
+
+ dispatch({
+ type: "discovery/fetchEnumType"
+ })
+
dispatch({
type: "pluginHandle/fetchByPluginId",
payload: {
@@ -106,6 +130,20 @@ class AddModal extends Component {
isHandleArray: multiSelectorHandle,
callBack: pluginHandles => {
this.setPluginHandleList(pluginHandles);
+ if (isDiscovery && Object.keys(pluginHandles).length > 0) {
+ const filteredArray = pluginHandles[0].filter(item => item.field
!== 'discoveryHandler');
+
+ const handlerArray = pluginHandles[0].filter(item => item.field
=== 'discoveryHandler');
+ this.setState({discoveryHandler: handlerArray});
+
+ pluginHandles[0] = filteredArray;
+ this.setState({ pluginHandleList: pluginHandles });
+
+ if (handlerArray.length !== 0) {
+ let defaultValue = handlerArray[0].defaultValue;
+ this.setState({ defaultValueList: defaultValue.split(",") });
+ }
+ }
}
}
});
@@ -137,16 +175,22 @@ class AddModal extends Component {
this.initDic("operator");
this.initDic("matchMode");
this.initDic("paramType");
+ this.initDic("discoveryMode");
};
initDic = type => {
- const { dispatch } = this.props;
+ const { dispatch, isAdd = true} = this.props;
dispatch({
type: "shenyuDict/fetchByType",
payload: {
type,
callBack: dics => {
this.state[`${type}Dics`] = dics;
+ if (type === "discoveryMode" && isAdd ) {
+ let configProps = dics.filter(item => item.dictName ===
'zookeeper');
+ let propsEntries = JSON.parse(configProps[0]?.dictValue || "{}");
+ this.setState({configPropsJson: propsEntries});
+ }
}
}
});
@@ -187,8 +231,8 @@ class AddModal extends Component {
handleSubmit = e => {
e.preventDefault();
- const { form, handleOk, multiSelectorHandle, pluginId } = this.props;
- const { selectorConditions, selectValue, pluginHandleList } = this.state;
+ const { form, handleOk, multiSelectorHandle, pluginId, isDiscovery } =
this.props;
+ const { selectorConditions, selectValue, pluginHandleList,
defaultValueList, configPropsJson, upstreams, importedDiscoveryId } =
this.state;
let handle = [];
form.validateFieldsAndScroll((err, values) => {
@@ -196,38 +240,67 @@ class AddModal extends Component {
const mySubmit =
selectValue !== "0" && this.checkConditions(selectorConditions);
if (mySubmit || selectValue === "0") {
- pluginHandleList.forEach((handleList, index) => {
- handle[index] = {};
- handleList.forEach(item => {
- if (pluginId === "8") {
- const { keys, divideUpstreams } = values;
- const data = {
- [item.field]: values[item.field],
- gray: values.gray
- };
-
- if (Array.isArray(divideUpstreams) && divideUpstreams.length) {
- data.divideUpstreams = keys.map(key => divideUpstreams[key]);
+ if ( isDiscovery ) {
+ // The discoveryProps refer to the attributes corresponding to
each registration center mode
+ const discoveryPropsJson = {};
+ Object.entries(configPropsJson).forEach(([key]) => {
+ discoveryPropsJson[key] = form.getFieldValue(key);
+ });
+ const discoveryProps = JSON.stringify(discoveryPropsJson);
+
+ // The handler refers to the url, status, weight, protocol, etc.
of the discovery module.
+ let handler = {};
+ if ( defaultValueList !== null) {
+ defaultValueList.forEach(item => {
+ if ((values[item]) !== undefined){
+ handler[values[item]] = item;
}
- handle[index] = data;
- delete values[item.field];
- delete values.divideUpstreams;
- delete values.gray;
- delete values.key;
- } else {
- handle[index][item.field] = values[item.field + index];
- delete values[item.field + index];
- }
+ });
+ }
+ handler = JSON.stringify(handler);
+
+ handleOk({...values,
+ sort: Number(values.sort),
+ selectorConditions,
+ handler,
+ discoveryProps,
+ upstreams,
+ importedDiscoveryId});
+
+ } else {
+ pluginHandleList.forEach((handleList, index) => {
+ handle[index] = {};
+ handleList.forEach(item => {
+ if (pluginId === "8") {
+ const {keys, divideUpstreams} = values;
+ const data = {
+ [item.field]: values[item.field],
+ gray: values.gray
+ };
+
+ if (Array.isArray(divideUpstreams) &&
divideUpstreams.length) {
+ data.divideUpstreams = keys.map(key =>
divideUpstreams[key]);
+ }
+ handle[index] = data;
+ delete values[item.field];
+ delete values.divideUpstreams;
+ delete values.gray;
+ delete values.key;
+ } else {
+ handle[index][item.field] = values[item.field + index];
+ delete values[item.field + index];
+ }
+ });
+ });
+ handleOk({
+ ...values,
+ handle: multiSelectorHandle
+ ? JSON.stringify(handle)
+ : JSON.stringify(handle[0]),
+ sort: Number(values.sort),
+ selectorConditions
});
- });
- handleOk({
- ...values,
- handle: multiSelectorHandle
- ? JSON.stringify(handle)
- : JSON.stringify(handle[0]),
- sort: Number(values.sort),
- selectorConditions
- });
+ }
}
}
});
@@ -332,10 +405,15 @@ class AddModal extends Component {
const {
form: { getFieldDecorator, getFieldValue, setFieldsValue },
multiSelectorHandle,
- pluginId
+ pluginId,
+ isDiscovery
} = this.props;
const labelWidth = 75;
+ if (isDiscovery) {
+ return;
+ }
+
if (pluginId === "8") {
getFieldDecorator("keys", {
initialValue: Array.from({
@@ -754,10 +832,43 @@ class AddModal extends Component {
this.setState({ visible: false, selectValue: formData.type });
};
+ handleImportDiscoveryConfig = configData => {
+ const { form } = this.props;
+ const {
+ type = '',
+ serverList = '',
+ id: discoveryId = '',
+ props = '{}'
+ } = configData || {};
+ const formData = {
+ selectedDiscoveryType: type,
+ serverList
+ };
+ this.setState(
+ { selectedDiscoveryValue: type, showDiscoveryImportModal: false,
importedDiscoveryId: discoveryId },
+ () => {
+ form.setFieldsValue(formData);
+ this.state.configPropsJson = JSON.parse(props);
+ }
+ );
+ };
+
onDealChange = (value, item) => {
item.value = value;
};
+ handleTableChange = (newData) => {
+ this.setState({ upstreams: newData });
+ };
+
+ handleCountChange = (newCount) => {
+ this.setState({ recordCount: newCount });
+ };
+
+ handleDiscoveryValueChange = (value) => {
+ this.setState({selectedDiscoveryValue: value})
+ }
+
renderOperatorOptions = (operators, paramType) => {
if (operators && operators instanceof Array) {
let operatorsFil = operators.map(operate => {
@@ -856,9 +967,18 @@ class AddModal extends Component {
}
}
- render() {
+ handleOptions() {
+ const { discovery } = this.props;
+ if (!discovery || !Array.isArray(discovery.typeEnums)) {
+ return [];
+ }
+ return discovery.typeEnums.map(type =>
+ <Option key={type} value={type.toString()}>{type.toString()}</Option>
+ )
+ }
+
+ renderBasicConfig = () => {
let {
- onCancel,
form,
name = "",
platform,
@@ -882,278 +1002,514 @@ class AddModal extends Component {
type = `${type.toString()}` || "1";
let { selectorTypeEnums } = platform;
-
const { getFieldDecorator } = form;
-
- return (
- <Modal
- width='900px'
- centered
- title={getIntlContent("SHENYU.SELECTOR.NAME")}
- visible
- okText={getIntlContent("SHENYU.COMMON.SURE")}
- cancelText={getIntlContent("SHENYU.COMMON.CALCEL")}
- onOk={this.handleSubmit}
- onCancel={onCancel}
- >
- <Form onSubmit={this.handleSubmit} className="login-form">
- <Item
- label={getIntlContent("SHENYU.PLUGIN.SELECTOR.LIST.COLUMN.NAME")}
- {...formItemLayout}
- >
- {getFieldDecorator("name", {
- rules: [
- {
+ return(
+ <>
+ <Item
+ label={getIntlContent("SHENYU.PLUGIN.SELECTOR.LIST.COLUMN.NAME")}
+ {...formItemLayout}
+ >
+ {getFieldDecorator("name", {
+ rules: [
+ {
required: true,
message: getIntlContent("SHENYU.COMMON.INPUTNAME")
- }
- ],
- initialValue: name
- })(
- <Input
- allowClear
- placeholder={getIntlContent(
- "SHENYU.PLUGIN.SELECTOR.LIST.COLUMN.NAME"
- )}
- addonAfter={
- <Button
- size="small"
- type="link"
- onClick={() => {
- this.setState({ visible: true });
- }}
- >
- {getIntlContent("SHENYU.SELECTOR.COPY")}
- </Button>
- }
- />
+ }
+ ],
+ initialValue: name
+ })(
+ <Input
+ allowClear
+ placeholder={getIntlContent(
+ "SHENYU.PLUGIN.SELECTOR.LIST.COLUMN.NAME"
)}
- </Item>
- <SelectorCopy
- visible={visible}
- onOk={this.handleCopyData}
- onCancel={() => {
- this.setState({ visible: false });
- }}
+ addonAfter={
+ <Button
+ size="small"
+ type="link"
+ onClick={() => {
+ this.setState({ visible: true });
+ }}
+ >
+ {getIntlContent("SHENYU.SELECTOR.COPY")}
+ </Button>
+ }
/>
- <Item
- label={getIntlContent("SHENYU.COMMON.TYPE")}
- {...formItemLayout}
- >
- {getFieldDecorator("type", {
+ )}
+ </Item>
+ <SelectorCopy
+ visible={visible}
+ onOk={this.handleCopyData}
+ onCancel={() => {
+ this.setState({ visible: false });
+ }}
+ />
+ <Item
+ label={getIntlContent("SHENYU.COMMON.TYPE")}
+ {...formItemLayout}
+ >
+ {getFieldDecorator("type", {
rules: [
- {
- required: true,
- message: getIntlContent("SHENYU.COMMON.INPUTTYPE")
- }
+ {
+ required: true,
+ message: getIntlContent("SHENYU.COMMON.INPUTTYPE")
+ }
],
initialValue: type
- })(
- <Select
- placeholder={getIntlContent("SHENYU.COMMON.TYPE")}
- onChange={value => this.getSelectValue(value)}
- >
- {selectorTypeEnums.map(item => {
- return (
- <Option key={item.code} value={item.code.toString()}>
- {getIntlContent(
-
`SHENYU.COMMON.SELECTOR.TYPE.${item.name.toUpperCase()}`,
- item.name
- )}
- </Option>
- );
- })}
- </Select>
- )}
- </Item>
- {selectValue !== "0" && (
- <Fragment>
- <Item
- label={getIntlContent("SHENYU.COMMON.MATCHTYPE")}
- {...formItemLayout}
- >
- {getFieldDecorator("matchMode", {
- rules: [
+ })(
+ <Select
+ placeholder={getIntlContent("SHENYU.COMMON.TYPE")}
+ onChange={value => this.getSelectValue(value)}
+ >
+ {selectorTypeEnums.map(item => {
+ return (
+ <Option key={item.code} value={item.code.toString()}>
+ {getIntlContent(
+ `SHENYU.COMMON.SELECTOR.TYPE.${item.name.toUpperCase()}`,
+ item.name
+ )}
+ </Option>
+ );
+ })}
+ </Select>
+ )}
+ </Item>
+ {selectValue !== "0" && (
+ <Fragment>
+ <Item
+ label={getIntlContent("SHENYU.COMMON.MATCHTYPE")}
+ {...formItemLayout}
+ >
+ {getFieldDecorator("matchMode", {
+ rules: [
{
- required: true,
- message: getIntlContent("SHENYU.COMMON.INPUTMATCHTYPE")
+ required: true,
+ message: getIntlContent("SHENYU.COMMON.INPUTMATCHTYPE")
}
- ],
- initialValue: `${matchMode}`
- })(
- <Select
- placeholder={getIntlContent("SHENYU.COMMON.MATCHTYPE")}
- >
- {matchModeDics &&
- matchModeDics.map(item => {
- return (
- <Option key={item.dictValue} value={item.dictValue}>
- {item.dictName}
- </Option>
- );
- })}
- </Select>
- )}
- </Item>
- <div className={styles.condition}>
- <Item
- label={getIntlContent("SHENYU.COMMON.CONDITION")}
- required
- {...formItemLayout}
+ ],
+ initialValue: `${matchMode}`
+ })(
+ <Select
+ placeholder={getIntlContent("SHENYU.COMMON.MATCHTYPE")}
>
- {selectorConditions.map((item, index) => {
- return (
- <Row key={index} gutter={8}>
- <Col span={5}>
- <Select
- onChange={value => {
+ {matchModeDics &&
+ matchModeDics.map(item => {
+ return (
+ <Option key={item.dictValue} value={item.dictValue}>
+ {item.dictName}
+ </Option>
+ );
+ })}
+ </Select>
+ )}
+ </Item>
+ <div className={styles.condition}>
+ <Item
+ label={getIntlContent("SHENYU.COMMON.CONDITION")}
+ required
+ {...formItemLayout}
+ >
+ {selectorConditions.map((item, index) => {
+ return (
+ <Row key={index} gutter={8}>
+ <Col span={5}>
+ <Select
+ onChange={value => {
this.conditionChange(index, "paramType", value);
- }}
- value={item.paramType}
- >
- {this.renderParamTypeOptions(paramTypeDics)}
- </Select>
- </Col>
- <Col
- span={4}
- style={{
- display: this.state[`paramTypeValueEn${index}`]
- ? "none"
- : "block"
}}
+ value={item.paramType}
>
- <Input
- allowClear
- onChange={e => {
- this.conditionChange(
+ {this.renderParamTypeOptions(paramTypeDics)}
+ </Select>
+ </Col>
+ <Col
+ span={4}
+ style={{
+ display: this.state[`paramTypeValueEn${index}`]
+ ? "none"
+ : "block"
+ }}
+ >
+ <Input
+ allowClear
+ onChange={e => {
+ this.conditionChange(
index,
"paramName",
e.target.value
- );
- }}
- placeholder={item.paramName}
- />
- </Col>
- <Col span={4}>
- <Select
- onChange={value => {
- this.conditionChange(index, "operator", value);
- }}
- value={item.operator}
- >
- {this.renderOperatorOptions(operatorDics,
item.paramType)}
- </Select>
- </Col>
-
- <Col
- span={7}
- style={{
- display: item.operator === "isBlank"
- ? "none"
- : "block"
+ );
+ }}
+ placeholder={item.paramName}
+ />
+ </Col>
+ <Col span={4}>
+ <Select
+ onChange={value => {
+ this.conditionChange(index, "operator", value);
}}
+ value={item.operator}
>
- <Tooltip title={item.paramValue}>
- {this.getParamValueInput(item, index)}
- </Tooltip>
- </Col>
- <Col span={4}>
- <Button
- type="danger"
- onClick={() => {
+ {this.renderOperatorOptions(operatorDics,
item.paramType)}
+ </Select>
+ </Col>
+
+ <Col
+ span={7}
+ style={{
+ display: item.operator === "isBlank"
+ ? "none"
+ : "block"
+ }}
+ >
+ <Tooltip title={item.paramValue}>
+ {this.getParamValueInput(item, index)}
+ </Tooltip>
+ </Col>
+ <Col span={4}>
+ <Button
+ type="danger"
+ onClick={() => {
this.handleDelete(index);
- }}
- style={{ marginLeft: 10 }}
- >
- {getIntlContent("SHENYU.COMMON.DELETE.NAME")}
- </Button>
- </Col>
- </Row>
- );
+ }}
+ style={{ marginLeft: 10 }}
+ >
+ {getIntlContent("SHENYU.COMMON.DELETE.NAME")}
+ </Button>
+ </Col>
+ </Row>
+ );
})}
- </Item>
- <Item
- label={' '}
- colon={false}
- {...formItemLayout}
- >
- <Button className={styles.addButton}
onClick={this.handleAdd} type="primary">
- {getIntlContent("SHENYU.COMMON.ADD")} {" "}
- {getIntlContent("SHENYU.COMMON.CONDITION")}
- </Button>
- </Item>
- </div>
-
- </Fragment>
- )}
- <div className={styles.layout}>
- <Item
- {...formCheckLayout}
- label={getIntlContent("SHENYU.SELECTOR.CONTINUE")}
- >
- {getFieldDecorator("continued", {
- initialValue: continued,
- valuePropName: "checked",
- rules: [{ required: true }]
- })(<Switch />)}
- </Item>
- <Item
- style={{ margin: "0 30px" }}
- {...formCheckLayout}
- label={getIntlContent("SHENYU.SELECTOR.PRINTLOG")}
- >
- {getFieldDecorator("loged", {
- initialValue: loged,
- valuePropName: "checked",
- rules: [{ required: true }]
- })(<Switch />)}
- </Item>
- <Item
- {...formCheckLayout}
- label={getIntlContent("SHENYU.SELECTOR.WHETHEROPEN")}
- >
- {getFieldDecorator("enabled", {
- initialValue: enabled,
- valuePropName: "checked",
- rules: [{ required: true }]
- })(<Switch />)}
- </Item>
- <Item
- style={{ margin: "0 30px" }}
- {...formCheckLayout}
- label={getIntlContent("SHENYU.SELECTOR.MATCHRESTFUL")}
- >
- {getFieldDecorator("matchRestful", {
+ </Item>
+ <Item
+ label={' '}
+ colon={false}
+ {...formItemLayout}
+ >
+ <Button className={styles.addButton} onClick={this.handleAdd}
type="primary">
+ {getIntlContent("SHENYU.COMMON.ADD")} {" "}
+ {getIntlContent("SHENYU.COMMON.CONDITION")}
+ </Button>
+ </Item>
+ </div>
+ </Fragment>
+ )}
+ <div className={styles.layout}>
+ <Item
+ {...formCheckLayout}
+ label={getIntlContent("SHENYU.SELECTOR.CONTINUE")}
+ >
+ {getFieldDecorator("continued", {
+ initialValue: continued,
+ valuePropName: "checked",
+ rules: [{ required: true }]
+ })(<Switch />)}
+ </Item>
+ <Item
+ style={{ margin: "0 30px" }}
+ {...formCheckLayout}
+ label={getIntlContent("SHENYU.SELECTOR.PRINTLOG")}
+ >
+ {getFieldDecorator("loged", {
+ initialValue: loged,
+ valuePropName: "checked",
+ rules: [{ required: true }]
+ })(<Switch />)}
+ </Item>
+ <Item
+ {...formCheckLayout}
+ label={getIntlContent("SHENYU.SELECTOR.WHETHEROPEN")}
+ >
+ {getFieldDecorator("enabled", {
+ initialValue: enabled,
+ valuePropName: "checked",
+ rules: [{ required: true }]
+ })(<Switch />)}
+ </Item>
+ <Item
+ style={{ margin: "0 30px" }}
+ {...formCheckLayout}
+ label={getIntlContent("SHENYU.SELECTOR.MATCHRESTFUL")}
+ >
+ {getFieldDecorator("matchRestful", {
initialValue: matchRestful,
valuePropName: "checked",
rules: [{ required: true }]
- })(<Switch />)}
- </Item>
- </div>
- {this.renderPluginHandler()}
- <Item
- label={getIntlContent("SHENYU.SELECTOR.EXEORDER")}
- {...formItemLayout}
- >
- {getFieldDecorator("sort", {
- initialValue: sort,
- rules: [
- {
+ })(<Switch />)}
+ </Item>
+ </div>
+ {this.renderPluginHandler()}
+ <Item
+ label={getIntlContent("SHENYU.SELECTOR.EXEORDER")}
+ {...formItemLayout}
+ >
+ {getFieldDecorator("sort", {
+ initialValue: sort,
+ rules: [
+ {
required: true,
message: getIntlContent("SHENYU.SELECTOR.INPUTNUMBER")
- },
- {
+ },
+ {
pattern: /^([1-9][0-9]{0,2}|1000)$/,
message: getIntlContent("SHENYU.SELECTOR.INPUTNUMBER")
- }
- ]
- })(
- <Input
- allowClear
- placeholder={getIntlContent("SHENYU.SELECTOR.INPUTORDER")}
+ }
+ ]
+ })(
+ <Input
+ allowClear
+ placeholder={getIntlContent("SHENYU.SELECTOR.INPUTORDER")}
+ />
+ )}
+ </Item>
+ </>
+ )
+ }
+
+ renderDiscoveryConfig = () => {
+ const { form, isAdd = true, discoveryConfig = {} } = this.props;
+ const { discoveryModeDics, upstreams, recordCount, discoveryHandler,
defaultValueList, configPropsJson, selectedDiscoveryValue } = this.state;
+ const { getFieldDecorator } = form;
+ return(
+ <>
+ <Item label={getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE")}
{...formItemLayout}>
+ {getFieldDecorator('selectedDiscoveryType', {
+ rules: [{required: true, message:
getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE.INPUT")}],
+ initialValue: discoveryConfig.discoveryType !== '' ?
discoveryConfig.discoveryType : 'local'
+ })(
+ <Select
+
placeholder={getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE.INPUT")}
+ disabled={!isAdd}
+ onChange={value => {
+ this.handleDiscoveryValueChange(value)
+ let configProps = discoveryModeDics.filter(item =>
item.dictName === value);
+ let propsEntries = JSON.parse(configProps[0]?.dictValue ||
"{}");
+ this.setState({configPropsJson: propsEntries})
+ }
+ }
+ >
+ {this.handleOptions()}
+ </Select>,
+ )}
+ </Item>
+
+ {
+ selectedDiscoveryValue !== 'local' ? (
+ <>
+ <Item
label={getIntlContent("SHENYU.DISCOVERY.SELECTOR.LISTENERNODE")}
{...formItemLayout}>
+ {getFieldDecorator('listenerNode', {
+ rules: [{required: true, message:
getIntlContent("SHENYU.DISCOVERY.SELECTOR.LISTENERNODE.INPUT")}],
+ initialValue: discoveryConfig.listenerNode
+ })(<Input
+ allowClear
+ disabled={!isAdd}
+
placeholder={getIntlContent("SHENYU.DISCOVERY.SELECTOR.LISTENERNODE.INPUT")}
+ />)}
+ </Item>
+
+ {discoveryHandler !== null && Array.isArray(discoveryHandler) &&
discoveryHandler.length !== 0 && (
+ <Item
label={getIntlContent("SHENYU.DISCOVERY.SELECTOR.HANDLER")} {...formItemLayout}>
+ <div
+ className={styles.handleWrap}
+ style={{
+ display: "flex"
+ }}
+ >
+ <div
+ style={{
+ display: "flex",
+ justifyContent: "space-between",
+ flexDirection: "row"
+ }}
+ >
+ <ul
+ className={classnames({
+ [styles.handleUl]: true,
+ [styles.springUl]: true
+ })}
+ style={{ width: "100%" }}
+ >
+ {(() => {
+ let item = discoveryHandler[0];
+ let checkRule = item.checkRule;
+ let required = item.required === "1";
+ let rules = [];
+ if (required) {
+ rules.push({
+ required: { required },
+ message:
+ getIntlContent("SHENYU.COMMON.PLEASEINPUT") +
+ item.label
+ });
+ }
+ if (checkRule) {
+ rules.push({
+ // eslint-disable-next-line no-eval
+ pattern: eval(checkRule),
+ message: `${getIntlContent(
+ "SHENYU.PLUGIN.RULE.INVALID"
+ )}:(${checkRule})`
+ });
+ }
+ if (defaultValueList != null) {
+ return defaultValueList.map((value, index) => (
+ <li key={index}>
+ <Item>
+ {getFieldDecorator(value, {
+ initialValue: isAdd === true ?
findKeyByValue(discoveryConfig.handler, value):
findKeyByValue(JSON.parse(discoveryConfig.handler), value),
+ rules
+ })(
+ <Input
+ allowClear
+ disabled={!isAdd}
+ addonAfter={
+ <div style={{ width: '50px' }}>
+ {value}
+ </div>
+ }
+ placeholder={`Your ${value}`}
+ key={value}
+ />
+ )}
+ </Item>
+ </li>
+ ));
+ }
+ })()}
+ </ul>
+ </div>
+ </div>
+ </Item>
+ )}
+
+ <Item
label={getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.SERVERLIST")}
{...formItemLayout}>
+ {getFieldDecorator('serverList', {
+ rules: [{required: true, message:
getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.SERVERLIST.INPUT")}],
+ initialValue: discoveryConfig.serverList
+ })(<Input
+ allowClear
+ disabled={!isAdd}
+
placeholder={getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.SERVERLIST.INPUT")}
+ />)}
+ </Item>
+
+ {Object.keys(configPropsJson).length > 0 && (
+ <div style={{ marginLeft: '60px', marginTop: '15px',
marginBottom: '15px', fontWeight: '500' }}>
+ {getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.PROPS")}
+ <span style={{ marginLeft: '2px', fontWeight: '500'
}}>:</span>
+ </div>
+ )}
+
+ <div style={{ marginLeft: '40px', marginRight: '35px', display:
'flex', alignItems: 'baseline' }}>
+ <div style={{ marginLeft: '8px', width: '100%' }}>
+ <Row gutter={[16, 4]} justify="center">
+ {Object.entries(configPropsJson).map(([key, value]) => (
+ <Col span={12} key={key}>
+ <Item>
+ {getFieldDecorator(key, {
+ initialValue: value
+ })(
+ <Input
+ allowClear
+ disabled={!isAdd}
+ placeholder={isAdd ? `Enter ${key}` : ''}
+ addonBefore={key}
+ style={{ width: '100%' }}
+ />
+ )}
+ </Item>
+ </Col>
+ ))}
+ </Row>
+ </div>
+ </div>
+
+ {
+ isAdd !== true ? (
+ <>
+
<Divider>{getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM")}</Divider>
+ <EditableFormTable
+ isLocal={false}
+ dataSource={upstreams}
+ recordCount={recordCount}
+ onTableChange={this.handleTableChange}
+ onCountChange={this.handleCountChange}
+ />
+ {/* <Table dataSource={upstreams} columns={columns} />; */}
+ </>
+ ):null
+ }
+ </>
+ ) : (
+ <>
+
<Divider>{getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM")}</Divider>
+ <EditableFormTable
+ isLocal={true}
+ dataSource={upstreams}
+ recordCount={recordCount}
+ onTableChange={this.handleTableChange}
+ onCountChange={this.handleCountChange}
/>
- )}
- </Item>
+ </>
+ )
+ }
+ </>
+ )
+
+ }
+
+
+ render() {
+ const {
+ onCancel,
+ isDiscovery,
+ isAdd = true
+ } = this.props;
+ const operations = (
+ <Button
+ type="primary"
+ icon="import"
+ onClick={() => {
+ this.setState({ showDiscoveryImportModal: true });
+ }}
+ disabled={!isAdd}
+ >
+ {getIntlContent("SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT")}
+ </Button>
+ );
+ return (
+ <Modal
+ width='1100px'
+ centered
+ title={getIntlContent("SHENYU.SELECTOR.NAME")}
+ // visible here defaults to true, because the visibility of modal is
determined by the popup attribute in index.js
+ visible
+ okText={getIntlContent("SHENYU.COMMON.SURE")}
+ cancelText={getIntlContent("SHENYU.COMMON.CALCEL")}
+ onOk={this.handleSubmit}
+ onCancel={onCancel}
+ >
+ <Form onSubmit={this.handleSubmit} className="login-form">
+ {// divide, grpc, websocket plugin
+ isDiscovery ? (
+ <Tabs defaultActiveKey="1" size="small"
tabBarExtraContent={operations}>
+ <TabPane
tab={getIntlContent("SHENYU.DISCOVERY.SELECTOR.CONFIG.BASIC")} key="1">
+ {this.renderBasicConfig()}
+ </TabPane>
+ <TabPane
tab={getIntlContent("SHENYU.DISCOVERY.SELECTOR.CONFIG.DISCOVERY")} key="2">
+ {this.renderDiscoveryConfig()}
+ <DiscoveryImportModal
+ pluginName={this.props.pluginName}
+ disabled={!isAdd}
+ visible={this.state.showDiscoveryImportModal}
+ onOk={this.handleImportDiscoveryConfig}
+ onCancel={() => {
+ this.setState({ showDiscoveryImportModal: false });
+ }}
+ />
+ </TabPane>
+ </Tabs>
+ ) : this.renderBasicConfig()
+ }
</Form>
</Modal>
);
diff --git a/src/routes/Plugin/Common/index.js
b/src/routes/Plugin/Common/index.js
index 1323218d..574a6619 100644
--- a/src/routes/Plugin/Common/index.js
+++ b/src/routes/Plugin/Common/index.js
@@ -65,7 +65,8 @@ export default class Common extends Component {
}
}
- componentDidUpdate(prevProps) {
+ /* eslint-disable no-unused-vars */
+ componentDidUpdate(prevProps, prevState, snapshot) {
const preId = prevProps.match.params.id;
const newId = this.props.match.params.id;
const { selectorPage, selectorPageSize } = this.state;
@@ -89,6 +90,7 @@ export default class Common extends Component {
}
}
}
+ /* eslint-enable no-unused-vars */
componentWillUnmount() {
const { dispatch } = this.props;
@@ -179,25 +181,96 @@ export default class Common extends Component {
const { id: pluginId, config } = plugin;
const multiSelectorHandle =
this.getPluginConfigField(config, "multiSelectorHandle") === "1";
- this.setState({
- popup: (
- <Selector
- pluginId={pluginId}
- multiSelectorHandle={multiSelectorHandle}
- handleOk={selector => {
- dispatch({
- type: "common/addSelector",
- payload: { pluginId, ...selector },
- fetchValue: { pluginId, currentPage: selectorPage, pageSize:
selectorPageSize },
- callback: () => {
- this.closeModal();
- }
- });
- }}
- onCancel={this.closeModal}
- />
- )
- });
+ const isDiscovery = ["5", "15", "26"].includes(pluginId);
+ if (isDiscovery) {
+ let discoveryConfig = {
+ discoveryType: '',
+ serverList: '',
+ handler: {},
+ listenerNode: '',
+ props: {}
+ }
+ let typeValue =
+ name === "divide" ? "http" :
+ name === "websocket" ? "ws" :
+ name === "grpc" ? "grpc" : "http";
+ this.setState({
+ popup: (
+ <Selector
+ pluginName={name}
+ pluginId={pluginId}
+ multiSelectorHandle={multiSelectorHandle}
+ isAdd={true}
+ discoveryConfig={discoveryConfig}
+ isDiscovery={true}
+ handleOk={selector => {
+ const { name: selectorName, listenerNode, serverList,
selectedDiscoveryType, discoveryProps, handler, upstreams, importedDiscoveryId
} = selector;
+ const upstreamsWithProps = upstreams.map(item => ({
+ protocol: item.protocol,
+ url: item.url,
+ status: parseInt(item.status, 10),
+ weight: item.weight,
+ startupTime: item.startupTime,
+ props: JSON.stringify({
+ warmupTime: item.warmupTime
+ })
+ }));
+ dispatch({
+ type: "common/addSelector",
+ payload: { pluginId, ...selector, upstreams:
upstreamsWithProps },
+ fetchValue: { pluginId, currentPage: selectorPage, pageSize:
selectorPageSize },
+ callback: (selectorId) => {
+ dispatch({
+ type: "discovery/bindSelector",
+ payload: {
+ selectorId,
+ name: selectorName,
+ pluginName: name,
+ listenerNode,
+ handler,
+ type: typeValue,
+ discoveryUpstreams: upstreamsWithProps,
+ discovery: {
+ id: importedDiscoveryId,
+ discoveryType: selectedDiscoveryType,
+ serverList,
+ props: discoveryProps,
+ name: selectorName
+ }
+ }
+ })
+ this.closeModal();
+ }
+ });
+ }}
+ onCancel={this.closeModal}
+ />
+ )
+ });
+ }else {
+ this.setState({
+ popup: (
+ <Selector
+ pluginName={name}
+ pluginId={pluginId}
+ multiSelectorHandle={multiSelectorHandle}
+ isDiscovery={false}
+ handleOk={selector => {
+ dispatch({
+ type: "common/addSelector",
+ payload: { pluginId, ...selector },
+ fetchValue: { pluginId, currentPage: selectorPage, pageSize:
selectorPageSize },
+ callback: () => {
+ this.closeModal();
+ }
+ });
+ }}
+ onCancel={this.closeModal}
+ />
+ )
+ });
+ }
+
};
searchRuleOnchange = e => {
@@ -297,6 +370,7 @@ export default class Common extends Component {
const { id: pluginId, config } = plugin;
const multiSelectorHandle =
this.getPluginConfigField(config, "multiSelectorHandle") === "1";
+ const isDiscovery = ["5", "15", "26"].includes(pluginId);
const { id } = record;
dispatch({
type: "common/fetchSeItem",
@@ -304,11 +378,37 @@ export default class Common extends Component {
id
},
callback: selector => {
+ if ( isDiscovery ){
+ let discoveryConfig = {
+ props: selector.discoveryVO ? selector.discoveryVO.props: "{}",
+ discoveryType: selector.discoveryVO ? selector.discoveryVO.type:
'local',
+ serverList: selector.discoveryVO ? selector.discoveryVO.serverList:
'',
+ handler: selector.discoveryHandler ?
selector.discoveryHandler.handler: "{}",
+ listenerNode: selector.discoveryHandler ?
selector.discoveryHandler.listenerNode : '',
+ }
+ let updateArray = [];
+ if (selector.discoveryUpstreams) {
+ updateArray = selector.discoveryUpstreams.map((item) => {
+ let propsObj = JSON.parse(item.props || "{}");
+ if (item.props === null) {
+ propsObj = {
+ warmupTime: 10,
+ };
+ }
+ return { ...item, key: item.id, warmupTime: propsObj.warmupTime };
+ });
+ }
+ let discoveryHandlerId = selector.discoveryHandler ?
selector.discoveryHandler.id : '';
this.setState({
popup: (
<Selector
+ pluginName={name}
{...selector}
multiSelectorHandle={multiSelectorHandle}
+ discoveryConfig={discoveryConfig}
+ discoveryUpstreams={updateArray}
+ isAdd={false}
+ isDiscovery={true}
handleOk={values => {
dispatch({
type: "common/updateSelector",
@@ -323,6 +423,24 @@ export default class Common extends Component {
pageSize: selectorPageSize
},
callback: () => {
+ const {upstreams} = values
+ const upstreamsWithHandlerId = upstreams.map(item => ({
+ protocol: item.protocol,
+ url: item.url,
+ status: parseInt(item.status, 10),
+ weight: item.weight,
+ props: JSON.stringify({
+ warmupTime: item.warmupTime
+ }),
+ discoveryHandlerId
+ }));
+ dispatch({
+ type: "discovery/updateDiscoveryUpstream",
+ payload: {
+ discoveryHandlerId,
+ upstreams: upstreamsWithHandlerId
+ }
+ })
this.closeModal();
}
});
@@ -331,6 +449,37 @@ export default class Common extends Component {
/>
)
});
+ } else {
+ this.setState({
+ popup: (
+ <Selector
+ pluginName={name}
+ {...selector}
+ multiSelectorHandle={multiSelectorHandle}
+ isDiscovery={false}
+ handleOk={values => {
+ dispatch({
+ type: "common/updateSelector",
+ payload: {
+ pluginId,
+ ...values,
+ id
+ },
+ fetchValue: {
+ pluginId,
+ currentPage: selectorPage,
+ pageSize: selectorPageSize
+ },
+ callback: () => {
+ this.closeModal();
+ }
+ });
+ }}
+ onCancel={this.closeModal}
+ />
+ )
+ });
+ }
}
});
};
diff --git a/src/routes/Plugin/Discovery/TcpCard.js
b/src/routes/Plugin/Discovery/DiscoveryCard.js
similarity index 92%
rename from src/routes/Plugin/Discovery/TcpCard.js
rename to src/routes/Plugin/Discovery/DiscoveryCard.js
index 55d79b12..93640896 100644
--- a/src/routes/Plugin/Discovery/TcpCard.js
+++ b/src/routes/Plugin/Discovery/DiscoveryCard.js
@@ -19,16 +19,16 @@ import React, {Component} from "react";
import {Card, Popover, Typography, Skeleton, Icon, Popconfirm} from "antd";
import {getIntlContent} from "../../../utils/IntlUtils";
-import tcpStyles from "./tcp.less";
+import discoveryStyles from "./discovery.less";
import { formatTimestamp } from "../../../utils/utils";
-import {ConsulIcon, EtcdIcon, LocalIcon, NacosIcon, ZkIcon} from
"./DiscoveryIcon";
+import {ConsulIcon, EtcdIcon, LocalIcon, NacosIcon, ZkIcon, EurekaIcon} from
"./DiscoveryIcon";
import AuthButton from "../../../utils/AuthButton";
const { Text } = Typography;
const { Meta } = Card;
-export class TcpCard extends Component {
+export class DiscoveryCard extends Component {
render() {
const { updateSelector, data, handleDelete, handleRefresh } = this.props
@@ -56,6 +56,7 @@ export class TcpCard extends Component {
nacos: <NacosIcon style={{ fontSize: '40px', color: '#354458' }} />,
consul: <ConsulIcon style={{ fontSize: '40px', color: '#354458' }} />,
etcd: <EtcdIcon style={{ fontSize: '40px', color: '#354458' }} />,
+ eureka: <EurekaIcon style={{ fontSize: '40px', color: '#354458' }} />,
};
const getAvatarIcon = () => {
@@ -67,7 +68,7 @@ export class TcpCard extends Component {
<Card
title={<div style={{ fontSize: '17px', lineHeight:
'1.5'}}>{data.name}</div>}
bordered={false}
- className={tcpStyles.tcpCard}
+ className={discoveryStyles.discoveryCard}
actions={[
<AuthButton perms="plugin:tcp:modify">
<Icon type="reload" key="reload" style={{color: '#2E496E',
fontSize: "17px"}} onClick={() => handleRefresh(data.discoveryHandlerId)} />
@@ -85,7 +86,7 @@ export class TcpCard extends Component {
>
<AuthButton perms="plugin:tcpSelector:delete">
<Icon type="delete" key="delete" style={{color: "#CC0000",
fontSize: "17px"}} />
- </AuthButton>,
+ </AuthButton>
</Popconfirm>
]}
extra={<div style={{ fontSize: '15px', lineHeight: '1.5',
marginRight: '14px'}}>{formatTimestamp(createTime)}</div>}
diff --git a/src/routes/Plugin/Discovery/DiscoveryConfigModal.js
b/src/routes/Plugin/Discovery/DiscoveryConfigModal.js
index 5c6be0fd..0ad11ff2 100644
--- a/src/routes/Plugin/Discovery/DiscoveryConfigModal.js
+++ b/src/routes/Plugin/Discovery/DiscoveryConfigModal.js
@@ -59,13 +59,13 @@ class DiscoveryConfigModal extends Component {
e.preventDefault();
form.validateFieldsAndScroll((err, values) => {
if (!err) {
- let { name, serverList, tcpType } = values;
+ let { name, serverList, discoveryType } = values;
const propsjson = {};
Object.entries(configPropsJson).forEach(([key]) => {
propsjson[key] = form.getFieldValue(key);
});
const props = JSON.stringify(propsjson);
- handleOk({ name, serverList, props, tcpType});
+ handleOk({ name, serverList, props, discoveryType});
}
});
};
@@ -82,7 +82,7 @@ class DiscoveryConfigModal extends Component {
render() {
const { handleCancel, form, data, isSetConfig, handleConfigDelete,
dispatch } = this.props
const { getFieldDecorator } = form;
- const { name, serverList, type: tcpType, id} = data || {};
+ const { name, serverList, type: discoveryType, id} = data || {};
const { configPropsJson, discoveryDicts } = this.state;
const formItemLayout = {
labelCol: {
@@ -109,8 +109,8 @@ class DiscoveryConfigModal extends Component {
placement="topLeft"
title={getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.DELETE")}
onConfirm={() => handleConfigDelete(id)}
- okText="Yes"
- cancelText="No"
+ okText={getIntlContent("SHENYU.COMMON.SURE")}
+ cancelText={getIntlContent("SHENYU.COMMON.CALCEL")}
key="popconfirm"
>
<Button key="delete" type="danger" style={{ marginRight: '10px'
}}>
@@ -129,9 +129,9 @@ class DiscoveryConfigModal extends Component {
>
<Form onSubmit={this.handleSubmit}>
<Form.Item
label={getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE")}
{...formItemLayout}>
- {getFieldDecorator('tcpType', {
+ {getFieldDecorator('discoveryType', {
rules: [{ required: true, message:
getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE.INPUT") }],
- initialValue: tcpType !== "" ? tcpType : undefined
+ initialValue: discoveryType !== "" ? discoveryType : undefined
})(
<Select
placeholder={getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE.INPUT")}
@@ -179,7 +179,7 @@ class DiscoveryConfigModal extends Component {
<span style={{ marginLeft: '2px', fontWeight: '500' }}>:</span>
</div>
<div style={{ marginLeft: '35px', display: 'flex', alignItems:
'baseline' }}>
- <div style={{ marginLeft: '8px' }}>
+ <div style={{ marginLeft: '8px', width: '100%' }}>
<Row gutter={[16, 4]} justify="center">
{Object.entries(configPropsJson).map(([key, value]) => (
<Col span={12} key={key}>
@@ -190,7 +190,7 @@ class DiscoveryConfigModal extends Component {
<Input
allowClear
disabled={isSetConfig}
- placeholder={`Enter ${key}`}
+ placeholder={!isSetConfig ? `Enter ${key}` : ''}
addonBefore={key}
/>
)}
diff --git a/src/routes/Plugin/Discovery/DiscoveryIcon.js
b/src/routes/Plugin/Discovery/DiscoveryIcon.js
index bd9edaf2..2f3fbcd4 100644
--- a/src/routes/Plugin/Discovery/DiscoveryIcon.js
+++ b/src/routes/Plugin/Discovery/DiscoveryIcon.js
@@ -100,11 +100,26 @@ const EtcdSvg = () => (
</svg>
)
+const EurekaSvg = () => (
+ <svg
+ className="icon"
+ viewBox="0 0 1024 1024"
+ width="1em"
+ height="1em"
+ >
+ <path
+ d="M985.417388 0H38.457749C17.231069 0 0 17.231069 0
38.457749v947.084502c0 21.22668 17.231069 38.457749 38.457749
38.457749h947.084502c21.22668 0 38.332886-17.231069
38.457749-38.332886V38.457749C1023.875137 17.231069 1006.644068 0 985.417388
0zM744.182417
820.723326H279.69272V203.151811h449.506158v92.897939H390.32118V449.506158h311.158152v92.897939H390.32118v185.42129H744.182417v92.897939z"
+ fill="currentColor"
+ />
+ </svg>
+)
+
export const ZkIcon = props => <Icon component={ZkSvg} {...props} />;
export const LocalIcon = props => <Icon component={LocalSvg} {...props} />;
export const NacosIcon = props => <Icon component={NacosSvg} {...props} />;
export const ConsulIcon = props => <Icon component={ConsulSvg} {...props} />;
export const EtcdIcon = props => <Icon component={EtcdSvg} {...props} />;
+export const EurekaIcon = props => <Icon component={EurekaSvg} {...props} />;
diff --git a/src/routes/Plugin/Discovery/DiscoveryImportModal.js
b/src/routes/Plugin/Discovery/DiscoveryImportModal.js
new file mode 100644
index 00000000..7e9c03f6
--- /dev/null
+++ b/src/routes/Plugin/Discovery/DiscoveryImportModal.js
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React, { Component } from "react";
+import { Modal } from "antd";
+import { connect } from "dva";
+import { getIntlContent } from "../../../utils/IntlUtils";
+
+@connect(({ discovery }) => ({
+ discovery
+}))
+
+class DiscoveryImportModal extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ configList: {},
+ loading: false
+ };
+ }
+
+ componentDidMount() {
+ const { dispatch, pluginName } = this.props
+ dispatch({
+ type: "discovery/fetchDiscovery",
+ payload: {
+ pluginName,
+ level: "1"
+ },
+ callback: discoveryConfigList => {
+ this.setState({configList: discoveryConfigList})
+ }
+ })
+ }
+
+ handleCancel = () => {
+ const { onCancel } = this.props;
+ // eslint-disable-next-line no-unused-expressions
+ onCancel && onCancel();
+ };
+
+ handleOk = async () => {
+ const { onOk } = this.props;
+ const { configList } = this.state;
+ // eslint-disable-next-line no-unused-expressions
+ onOk && onOk(configList);
+ };
+
+ render() {
+ const { visible = false } = this.props;
+ const { loading, configList } = this.state;
+ const { type = '', serverList = '', props: discoveryProps = '{}' } =
configList || {};
+ return (
+ <Modal
+ visible={visible}
+ centered
+
title={getIntlContent("SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT.CONFIRM")}
+ onCancel={this.handleCancel}
+ onOk={this.handleOk}
+ confirmLoading={loading}
+ okText={getIntlContent("SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT.SURE")}
+ cancelText={getIntlContent("SHENYU.COMMON.CALCEL")}
+ >
+ <ul>
+ <li style={{ marginBottom: '8px' }}>
+
<strong>{getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE")}:</strong> {type}
+ </li>
+ <li style={{ marginBottom: '8px' }}>
+
<strong>{getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.SERVERLIST")}:</strong>
{serverList}
+ </li>
+ <li>
+
<strong>{getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.PROPS")}:</strong>
<pre><code>{JSON.stringify(JSON.parse(discoveryProps), null, 4)}</code></pre>
+ </li>
+ </ul>
+ </Modal>
+ );
+ }
+}
+
+export default DiscoveryImportModal;
diff --git a/src/routes/Plugin/Discovery/DiscoveryUpstreamTable.js
b/src/routes/Plugin/Discovery/DiscoveryUpstreamTable.js
new file mode 100644
index 00000000..33357b71
--- /dev/null
+++ b/src/routes/Plugin/Discovery/DiscoveryUpstreamTable.js
@@ -0,0 +1,287 @@
+import React, {Component} from "react";
+import {Button, Form, Input, InputNumber, Popconfirm, Select, Table} from
'antd';
+import {getIntlContent} from "../../../utils/IntlUtils";
+
+const EditableContext = React.createContext();
+const { Option } = Select;
+
+class EditableCell extends Component {
+ getInput = () => {
+ if (this.props.inputType === 'number') {
+ return <InputNumber />;
+ } else if (this.props.inputType === 'dropdown') {
+ // return <Select />;
+ return (
+ <Select>
+ <Option value="0">open</Option>
+ <Option value="1">close</Option>
+ </Select>
+ );
+ }
+ return <Input />;
+ };
+
+ renderCell = ({ getFieldDecorator }) => {
+ const {
+ editing,
+ dataIndex,
+ title,
+ inputType,
+ record,
+ index,
+ children,
+ ...restProps
+ } = this.props;
+ return (
+ <td {...restProps}>
+ {editing ? (
+ <Form.Item style={{ margin: 0 }}>
+ {getFieldDecorator(dataIndex, {
+ rules: [
+ {
+ required: true,
+ message: `Please Input ${title}!`,
+ },
+ ],
+ initialValue: record[dataIndex],
+ })(this.getInput())}
+ </Form.Item>
+ ) : (
+ children
+ )}
+ </td>
+ );
+ };
+
+ render() {
+ return
<EditableContext.Consumer>{this.renderCell}</EditableContext.Consumer>;
+ }
+}
+
+class EditableTable extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ editingKey: '',
+ isLocal: this.props.isLocal
+ };
+ this.columns = [
+ {
+ title: 'protocol',
+ dataIndex: 'protocol',
+ editable: true,
+ width: '25%',
+ align: 'center'
+ },
+ {
+ title: 'url',
+ dataIndex: 'url',
+ editable: this.state.isLocal,
+ width: '35%',
+ align: 'center'
+ },
+ {
+ title: 'status',
+ dataIndex: 'status',
+ editable: true,
+ width: '19%',
+ align: 'center',
+ render: (text) => {
+ return text === 0 || text === '0' ? 'open' : 'close';
+ },
+ },
+ {
+ title: 'weight',
+ dataIndex: 'weight',
+ editable: true,
+ align: 'center'
+ },
+ {
+ title: 'startupTime',
+ dataIndex: 'startupTime',
+ editable: this.state.isLocal,
+ align: 'center'
+ },
+ {
+ title: 'warmupTime',
+ dataIndex: 'warmupTime',
+ editable: true,
+ align: 'center'
+ },
+ {
+ title: getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.OPERATION"),
+ dataIndex: 'operation',
+ width: '18%',
+ align: 'center',
+ render: (text, record) => {
+ const { editingKey } = this.state;
+ const editable = this.isEditing(record);
+ return (
+ <span>
+ {editable ? (
+ <span>
+ <EditableContext.Consumer>
+ {form => (
+ <a
+ onClick={() => this.save(form, record.key)}
+ style={{ marginRight: 8 }}
+ >
+
{getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.SAVE")}
+ </a>
+ )}
+ </EditableContext.Consumer>
+ <Popconfirm
+
title={getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.CANCEL.CONFIRM")}
+ onConfirm={() => this.cancel(record.key)}
+ okText={getIntlContent("SHENYU.COMMON.SURE")}
+ cancelText={getIntlContent("SHENYU.COMMON.CALCEL")}
+ >
+
<a>{getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.CANCEL")}</a>
+ </Popconfirm>
+ </span>
+ ) : (
+ <span>
+ <Button
+ type="link"
+ disabled={editingKey !== ''}
+ onClick={() => this.edit(record.key)}
+ >
+
{getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.EDIT")}
+ </Button>
+ {' '}
+ {this.props.dataSource.length >= 1 && this.state.isLocal ?
(
+ <Popconfirm
+
title={getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DELETE.CONFIRM")}
+ onConfirm={() => this.handleDelete(record.key)}
+ okText={getIntlContent("SHENYU.COMMON.SURE")}
+ cancelText={getIntlContent("SHENYU.COMMON.CALCEL")}
+ >
+ <a>{getIntlContent("SHENYU.BUTTON.SYSTEM.DELETE")}</a>
+ </Popconfirm>
+ ) : null}
+ </span>
+ )
+ }
+ </span>
+ );
+ },
+ },
+ ];
+ }
+
+ isEditing = record => record.key === this.state.editingKey;
+
+ cancel = () => {
+ this.setState({ editingKey: '' });
+ };
+
+ handleDelete = key => {
+ const { dataSource } = this.props;
+ const newData = dataSource.filter(item => item.key !== key);
+ this.props.onTableChange(newData);
+ };
+
+ handleAdd = () => {
+ const { dataSource, recordCount } = this.props;
+ const newRecordCount = recordCount + 1;
+ const newData = {
+ key: newRecordCount,
+ protocol: 'http://',
+ url: 'localhost:',
+ status: 0,
+ weight: 50,
+ startupTime: 0,
+ warmupTime: 10
+ };
+ this.props.onTableChange([...dataSource, newData]);
+ this.props.onCountChange(newRecordCount);
+ };
+
+ save(form, key) {
+ form.validateFields((error, row) => {
+ if (error) {
+ return;
+ }
+ const newData = [...this.props.dataSource];
+ const index = newData.findIndex(item => key === item.key);
+ if (index > -1) {
+ const item = newData[index];
+ newData.splice(index, 1, {
+ ...item,
+ ...row,
+ });
+ this.props.onTableChange(newData);
+ this.setState({ editingKey: '' });
+ } else {
+ const { recordCount } = this.props;
+ row.key = recordCount + 1;
+ newData.push(row);
+ this.props.onCountChange(recordCount + 1);
+ this.props.onTableChange(newData);
+ this.setState({ editingKey: '' });
+ }
+ });
+ }
+
+ edit(key) {
+ this.setState({ editingKey: key });
+ }
+
+ render() {
+ const components = {
+ body: {
+ cell: EditableCell,
+ },
+ };
+
+ const columns = this.columns.map(col => {
+ if (!col.editable) {
+ return col;
+ }
+
+ let inputType = 'text';
+ if (col.dataIndex === 'weight' || col.dataIndex === 'startupTime' ||
col.dataIndex === 'warmupTime') {
+ inputType = 'number';
+ } else if (col.dataIndex === 'status') {
+ inputType = 'dropdown';
+ }
+ return {
+ ...col,
+ onCell: record => ({
+ record,
+ inputType,
+ dataIndex: col.dataIndex,
+ title: col.title,
+ editing: this.isEditing(record),
+ }),
+ };
+ });
+
+ return (
+ <div>
+ {this.state.isLocal && (
+ <Button onClick={this.handleAdd} type="primary" style={{
marginBottom: 16 }}>
+ {getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.ADD")}
+ </Button>
+ )}
+ <EditableContext.Provider value={this.props.form}>
+ <Table
+ components={components}
+ bordered
+ dataSource={this.props.dataSource}
+ columns={columns}
+ rowClassName="editable-row"
+ pagination={{
+ onChange: this.cancel,
+ }}
+ />
+ </EditableContext.Provider>
+ </div>
+
+ );
+ }
+}
+
+const EditableFormTable = Form.create()(EditableTable);
+export default EditableFormTable;
+
diff --git a/src/routes/Plugin/Discovery/ProxySelectorModal.js
b/src/routes/Plugin/Discovery/ProxySelectorModal.js
index 7132bfd8..01e3cf1e 100644
--- a/src/routes/Plugin/Discovery/ProxySelectorModal.js
+++ b/src/routes/Plugin/Discovery/ProxySelectorModal.js
@@ -17,13 +17,13 @@
import React, {Component} from "react";
import {connect} from "dva";
-import {Button, Col, Divider, Form, Input, Modal, Row, Select, Table, Tabs,
Tooltip} from "antd";
+import {Button, Col, Divider, Form, Input, Modal, Row, Select, Tabs, Tooltip}
from "antd";
import classnames from "classnames";
import {getIntlContent} from "../../../utils/IntlUtils";
-import EditableTable from './UpstreamTable';
import styles from "../index.less";
import ProxySelectorCopy from "./ProxySelectorCopy.js";
import {findKeyByValue} from "../../../utils/utils";
+import EditableFormTable from "./DiscoveryUpstreamTable";
const FormItem = Form.Item;
@@ -53,7 +53,7 @@ class ProxySelectorModal extends Component {
}
componentDidMount() {
- const { isAdd, isSetConfig, tcpType, data, pluginId, dispatch } =
this.props;
+ const { isAdd, isSetConfig, discoveryType, data, pluginId, dispatch } =
this.props;
const { discoveryDicts } = this.state;
const { props } = this.props.data || {};
@@ -62,7 +62,7 @@ class ProxySelectorModal extends Component {
dispatch({
type: 'discovery/saveGlobalType',
payload: {
- chosenType: tcpType
+ chosenType: discoveryType
}
});
}else{
@@ -88,10 +88,13 @@ class ProxySelectorModal extends Component {
callBack: (pluginHandles) => {
if (Object.keys(pluginHandles).length > 0) {
const filteredArray = pluginHandles[0].filter(item => item.field
!== 'discoveryHandler');
+
const handlerArray = pluginHandles[0].filter(item => item.field
=== 'discoveryHandler');
- pluginHandles[0] = filteredArray;
this.setState({discoveryHandler: handlerArray});
+
+ pluginHandles[0] = filteredArray;
this.setState({ pluginHandleList: pluginHandles });
+
let defaultValue = handlerArray[0].defaultValue;
this.setState({ defaultValueList: defaultValue.split(",") });
}
@@ -106,12 +109,15 @@ class ProxySelectorModal extends Component {
e.preventDefault();
form.validateFieldsAndScroll((err, values) => {
if (!err) {
- let {name, forwardPort, listenerNode, serverList, discoveryType} =
values;
+ let {name, forwardPort, listenerNode, serverList,
selectedDiscoveryType} = values;
const discoveryPropsJson = {};
Object.entries(configPropsJson).forEach(([key]) => {
discoveryPropsJson[key] = form.getFieldValue(key);
});
+ // The discoveryProps refer to the attributes corresponding to each
registration center mode
const discoveryProps = JSON.stringify(discoveryPropsJson);
+
+ // The handler refers to the url, status, weight, protocol, etc. of
the discovery module.
let handler = {};
if ( defaultValueList !== null) {
defaultValueList.forEach(item => {
@@ -120,6 +126,8 @@ class ProxySelectorModal extends Component {
}
});
}
+ handler = JSON.stringify(handler);
+
let handleResult = [];
handleResult[0] = {};
if(Object.keys(pluginHandleList).length > 0){
@@ -127,9 +135,9 @@ class ProxySelectorModal extends Component {
handleResult[0][item.field] = values[item.field + 0];
});
}
- handler = JSON.stringify(handler);
+ // The props refer to the properties of each plug-in
let props = JSON.stringify(handleResult[0]);
- handleOk({name, forwardPort, props, listenerNode, handler,
discoveryProps, serverList, discoveryType, upstreams});
+ handleOk({name, forwardPort, props, listenerNode, handler,
discoveryProps, serverList, selectedDiscoveryType, upstreams});
}
});
};
@@ -141,7 +149,7 @@ class ProxySelectorModal extends Component {
name,
forwardPort,
listenerNode,
- discoveryType: discovery.type,
+ selectedDiscoveryType: discovery.type,
serverList: discovery.serverList
};
dispatch({
@@ -176,7 +184,7 @@ class ProxySelectorModal extends Component {
render() {
- const { tcpType, form, handleCancel, isSetConfig, isAdd, chosenType,
dispatch } = this.props;
+ const { discoveryType, form, handleCancel, isSetConfig, isAdd, chosenType,
dispatch } = this.props;
const { recordCount, upstreams, pluginHandleList, visible,
discoveryHandler, defaultValueList, discoveryDicts, configPropsJson } =
this.state;
const { getFieldDecorator } = form;
const { name, forwardPort, listenerNode, discovery, handler } =
this.props.data || {};
@@ -189,32 +197,6 @@ class ProxySelectorModal extends Component {
sm: { span: 19 }
}
};
- const columns = [
- {
- title: 'protocol',
- dataIndex: 'protocol',
- key: 'protocol',
- align: 'center'
- },
- {
- title: 'url',
- dataIndex: 'url',
- key: 'url',
- align: 'center'
- },
- {
- title: 'status',
- dataIndex: 'status',
- key: 'status',
- align: 'center'
- },
- {
- title: 'weight',
- dataIndex: 'weight',
- key: 'weight',
- align: 'center'
- },
- ];
return (
<Modal
@@ -426,9 +408,9 @@ class ProxySelectorModal extends Component {
</TabPane>
<TabPane
tab={getIntlContent("SHENYU.DISCOVERY.SELECTOR.CONFIG.DISCOVERY")} key="2">
<FormItem
label={getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE")}
{...formItemLayout}>
- {getFieldDecorator('discoveryType', {
+ {getFieldDecorator('selectedDiscoveryType', {
rules: [{required: true, message:
getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE.INPUT")}],
- initialValue: tcpType !== '' ? tcpType : undefined
+ initialValue: discoveryType !== '' ? discoveryType :
undefined
})(
<Select
placeholder={getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE.INPUT")}
@@ -526,7 +508,7 @@ class ProxySelectorModal extends Component {
{value}
</div>
}
- placeholder={`Your ${value}`}
+ placeholder={isAdd? `Your
${value}` : ''}
key={value}
/>
)}
@@ -560,7 +542,7 @@ class ProxySelectorModal extends Component {
<span style={{ marginLeft: '2px', fontWeight:
'500' }}>:</span>
</div>
<div style={{ marginLeft: '35px', display: 'flex',
alignItems: 'baseline' }}>
- <div style={{ marginLeft: '8px' }}>
+ <div style={{ marginLeft: '8px', width: '100%' }}>
<Row gutter={[16, 4]} justify="center">
{Object.entries(configPropsJson).map(([key,
value]) => (
<Col span={12} key={key}>
@@ -571,7 +553,7 @@ class ProxySelectorModal extends Component {
<Input
allowClear
disabled={!isAdd}
- placeholder={`Enter ${key}`}
+ placeholder={isAdd ? `Enter ${key}`
: ''}
addonBefore={key}
/>
)}
@@ -589,7 +571,13 @@ class ProxySelectorModal extends Component {
isAdd !== true ? (
<>
<Divider>{getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM")}</Divider>
- <Table dataSource={upstreams} columns={columns} />;
+ <EditableFormTable
+ isLocal={false}
+ dataSource={upstreams}
+ recordCount={recordCount}
+ onTableChange={this.handleTableChange}
+ onCountChange={this.handleCountChange}
+ />
</>
):null
}
@@ -597,7 +585,8 @@ class ProxySelectorModal extends Component {
) : (
<>
<Divider>{getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM")}</Divider>
- <EditableTable
+ <EditableFormTable
+ isLocal={true}
dataSource={upstreams}
recordCount={recordCount}
onTableChange={this.handleTableChange}
diff --git a/src/routes/Plugin/Discovery/UpstreamTable.js
b/src/routes/Plugin/Discovery/UpstreamTable.js
deleted file mode 100644
index d1cc7f1a..00000000
--- a/src/routes/Plugin/Discovery/UpstreamTable.js
+++ /dev/null
@@ -1,213 +0,0 @@
-import React, {Component, createContext} from "react";
-import { Table, Input, Button, Popconfirm, Form } from 'antd';
-import AuthButton from "../../../utils/AuthButton";
-import {getIntlContent} from "../../../utils/IntlUtils";
-
-
-const EditableContext = createContext();
-
-const EditableRow = ({ form, index, ...props }) => (
- <EditableContext.Provider value={form}>
- <tr {...props} />
- </EditableContext.Provider>
-);
-
-const EditableFormRow = Form.create()(EditableRow);
-
-class EditableCell extends Component {
- state = {
- editing: false,
- };
-
- toggleEdit = () => {
- this.setState(prevState => ({ editing: !prevState.editing }), () => {
- if (this.state.editing) {
- this.input.focus();
- }
- });
- };
-
- save = e => {
- const { record, handleSave } = this.props;
- this.form.validateFields((error, values) => {
- if (error && error[e.currentTarget.id]) {
- return;
- }
- this.toggleEdit();
- handleSave({ ...record, ...values });
- });
- };
-
-
- renderCell = form => {
- this.form = form;
- const { children, dataIndex, record, title } = this.props;
- const { editing } = this.state;
- return editing ? (
- <Form.Item style={{ margin: 0 }}>
- {form.getFieldDecorator(dataIndex, {
- rules: [
- {
- required: true,
- message: `${title} is required.`,
- },
- ],
- initialValue: record[dataIndex],
- })(<Input allowClear ref={node => { this.input = node }}
onPressEnter={this.save} onBlur={this.save} />
- )}
- </Form.Item>
- ) : (
- <div
- className="editable-cell-value-wrap"
- style={{ paddingRight: 24 }}
- onClick={this.toggleEdit}
- >
- {children}
- </div>
- );
- };
-
- render() {
- const {
- editable,
- dataIndex,
- title,
- record,
- index,
- handleSave,
- children,
- ...restProps
- } = this.props;
- return (
- <td {...restProps}>
- {editable ? (
-
<EditableContext.Consumer>{this.renderCell}</EditableContext.Consumer>
- ) : (
- children
- )}
- </td>
- );
- }
-}
-
-
-export default class EditableTable extends Component {
- constructor(props) {
- super(props);
- this.columns = [
- {
- title: 'protocol',
- dataIndex: 'protocol',
- editable: true,
- // width: '20%',
- align: 'center'
- },
- {
- title: 'url',
- dataIndex: 'url',
- editable: true,
- width: '33%',
- align: 'center'
- },
- {
- title: 'status',
- dataIndex: 'status',
- editable: true,
- // width: '19%',
- align: 'center'
- },
- {
- title: 'weight',
- dataIndex: 'weight',
- editable: true,
- // width: '19%',
- align: 'center'
- },
- {
- title: getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.OPERATION"),
- dataIndex: 'operation',
- align: 'center',
- render: (text, record) =>
- this.props.dataSource.length >= 1 ? (
- <Popconfirm
title={getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DELETE")}
onConfirm={() => this.handleDelete(record.key)}>
- <a>{getIntlContent("SHENYU.BUTTON.SYSTEM.DELETE")}</a>
- </Popconfirm>
- ) : null,
- },
- ];
-
- }
-
- handleDelete = key => {
- const { dataSource } = this.props;
- const newData = dataSource.filter(item => item.key !== key);
- this.props.onTableChange(newData);
- };
-
- handleAdd = () => {
- const { dataSource, recordCount} = this.props;
- const newRecordCount = recordCount + 1;
- const newData = {
- key: newRecordCount,
- protocol: 'protocol',
- url: 'url',
- status: '0',
- weight: '0',
- };
- this.props.onTableChange([...dataSource, newData]);
- this.props.onCountChange(newRecordCount);
- };
-
- handleSave = row => {
- const newData = [...this.props.dataSource];
- const index = newData.findIndex(item => row.key === item.key);
- const item = newData[index];
- newData.splice(index, 1, {
- ...item,
- ...row,
- });
- this.props.onTableChange(newData);
- };
-
- render() {
- const { dataSource } = this.props;
- const components = {
- body: {
- row: EditableFormRow,
- cell: EditableCell,
- },
- };
- const columns = this.columns.map(col => {
- if (!col.editable) {
- return col;
- }
- return {
- ...col,
- onCell: record => ({
- record,
- editable: col.editable,
- dataIndex: col.dataIndex,
- title: col.title,
- handleSave: this.handleSave,
- }),
- };
- });
- return (
- <div>
- <AuthButton perms="plugin:tcpSelector:add">
- <Button onClick={this.handleAdd} type="primary" style={{
marginBottom: 16 }}>
- {getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.ADD")}
- </Button>
- </AuthButton>
- <Table
- components={components}
- rowClassName={() => 'editable-row'}
- bordered
- dataSource={dataSource}
- columns={columns}
- />
- </div>
- );
- }
-}
-
diff --git a/src/routes/Plugin/Discovery/tcp.less
b/src/routes/Plugin/Discovery/discovery.less
similarity index 99%
rename from src/routes/Plugin/Discovery/tcp.less
rename to src/routes/Plugin/Discovery/discovery.less
index ba2d5ae7..eddaaf46 100644
--- a/src/routes/Plugin/Discovery/tcp.less
+++ b/src/routes/Plugin/Discovery/discovery.less
@@ -79,7 +79,7 @@
}
-.tcpCard {
+.discoveryCard {
:global {
.ant-card-body {
padding: 10px
diff --git a/src/routes/Plugin/Discovery/index.js
b/src/routes/Plugin/Discovery/index.js
index 6d9efe9a..baaf7c6d 100644
--- a/src/routes/Plugin/Discovery/index.js
+++ b/src/routes/Plugin/Discovery/index.js
@@ -19,10 +19,10 @@ import React, {Component} from 'react';
import {connect} from 'dva';
import {Button, Pagination, Row, Switch, Tag, Input, Typography, message} from
"antd";
import {getIntlContent} from "../../../utils/IntlUtils";
-import tcpStyles from './tcp.less'
+import discoveryStyles from './discovery.less'
import DiscoveryConfigModal from "./DiscoveryConfigModal";
import ProxySelectorModal from "./ProxySelectorModal";
-import {TcpCard} from "./TcpCard";
+import {DiscoveryCard} from "./DiscoveryCard";
import AuthButton from "../../../utils/AuthButton";
import { getUpdateModal, updatePluginsEnabled } from "../../../utils/plugin";
@@ -35,13 +35,13 @@ const {Title} = Typography;
...shenyuDict,
loading: loading.effects["global/fetchPlatform"]
}))
-export default class TCPProxy extends Component {
+export default class DiscoveryProxy extends Component {
constructor(props) {
super(props);
this.state = {
searchKey: '',
cardData: {
- tcpType: '',
+ discoveryType: '',
name: '',
forwardPort: '',
type: 'tcp',
@@ -71,7 +71,7 @@ export default class TCPProxy extends Component {
componentDidMount() {
const {dispatch, currentPage, pageSize} = this.props
dispatch({
- type: "discovery/fetchSelector",
+ type: "discovery/fetchProxySelectors",
payload: {
name: '',
currentPage,
@@ -95,9 +95,9 @@ export default class TCPProxy extends Component {
}
// eslint-disable-next-line react/sort-comp
- renderCards(selectorList = []) {
- return selectorList.map(selector =>
- <TcpCard key={selector.id} updateSelector={this.updateSelector}
data={selector} handleDelete={this.handleDelete}
handleRefresh={this.handleRefresh} />
+ renderCards(proxySelectorList = []) {
+ return proxySelectorList.map(selector =>
+ <DiscoveryCard key={selector.id} updateSelector={this.updateSelector}
data={selector} handleDelete={this.handleDelete}
handleRefresh={this.handleRefresh} />
);
}
@@ -111,7 +111,7 @@ export default class TCPProxy extends Component {
})
const {searchKey} = this.state;
this.props.dispatch({
- type: "discovery/fetchSelector",
+ type: "discovery/fetchProxySelectors",
payload: {
currentPage: page,
pageSize,
@@ -152,7 +152,7 @@ export default class TCPProxy extends Component {
this.setState({popup: ""});
this.setState({
cardData: {
- tcpType: '',
+ discoveryType: '',
name: '',
forwardPort: '',
type: 'tcp',
@@ -163,15 +163,7 @@ export default class TCPProxy extends Component {
serverList: '',
props: {}
},
- discoveryUpstreams: [
- // {
- // protocol: '1',
- // url: '1',
- // status:'1',
- // weight: '1',
- // key: '1'
- // }
- ]
+ discoveryUpstreams: []
}
});
};
@@ -201,11 +193,11 @@ export default class TCPProxy extends Component {
discoveryDicts={discoveryDics}
handleConfigDelete={this.handleConfigDelete}
handleOk={values => {
- const {name, serverList, props, tcpType} = values;
+ const {name, serverList, props, discoveryType} = values;
dispatch({
type: "discovery/set",
payload: {
- type: tcpType,
+ type: discoveryType,
serverList,
name,
props,
@@ -257,7 +249,7 @@ export default class TCPProxy extends Component {
const {searchKey} = this.state;
const {currentPage, pageSize} = this.props
this.props.dispatch({
- type: "discovery/fetchSelector",
+ type: "discovery/fetchProxySelectors",
payload: {
currentPage,
pageSize,
@@ -278,11 +270,11 @@ export default class TCPProxy extends Component {
level: "1"
},
callback: discoveryConfigList => {
- let tcpType = '';
+ let discoveryType = '';
let id = null;
let isSetConfig = false;
if (discoveryConfigList !== null) {
- tcpType = discoveryConfigList.type;
+ discoveryType = discoveryConfigList.type;
id = discoveryConfigList.id
isSetConfig = true;
}
@@ -294,12 +286,22 @@ export default class TCPProxy extends Component {
typeEnums={typeEnums}
data={cardData}
discoveryUpstreams={cardData.discoveryUpstreams}
- tcpType={tcpType}
+ discoveryType={discoveryType}
isAdd={true}
isSetConfig={isSetConfig}
discoveryDicts={discoveryDics}
handleOk={values => {
- const {name, forwardPort, props, listenerNode, handler,
discoveryProps, serverList, discoveryType, upstreams} = values;
+ const {name, forwardPort, props, listenerNode, handler,
discoveryProps, serverList, selectedDiscoveryType, upstreams} = values;
+ const upstreamsWithProps = upstreams.map(item => ({
+ protocol: item.protocol,
+ url: item.url,
+ status: parseInt(item.status, 10),
+ weight: item.weight,
+ startupTime: item.startupTime,
+ props: JSON.stringify({
+ warmupTime: item.warmupTime
+ })
+ }));
dispatch({
type: 'discovery/add',
payload: {
@@ -314,11 +316,11 @@ export default class TCPProxy extends Component {
id,
level: "0", // 0 selector
pluginName,
- discoveryType,
+ discoveryType: selectedDiscoveryType,
serverList,
props: discoveryProps
},
- discoveryUpstreams: upstreams
+ discoveryUpstreams: upstreamsWithProps
},
callback: () => {
this.closeModal();
@@ -340,9 +342,9 @@ export default class TCPProxy extends Component {
}
updateSelector = (id) => {
- const {dispatch, selectorList, tcpType: discoveryType, currentPage,
pageSize, plugins, typeEnums} = this.props;
+ const {dispatch, proxySelectorList, discoveryType, currentPage, pageSize,
plugins, typeEnums} = this.props;
const { discoveryDics, pluginName } = this.state;
- const data = selectorList.find(value => value.id === id)
+ const data = proxySelectorList.find(value => value.id === id)
const plugin = this.getPlugin(plugins, pluginName);
let isSetConfig = false
this.setState({
@@ -352,14 +354,20 @@ export default class TCPProxy extends Component {
isSetConfig = true
}
const updateArray = data.discoveryUpstreams.map((item) => {
- return { ...item, key: item.id };
+ let propsObj = JSON.parse(item.props || "{}");
+ if (item.props === null) {
+ propsObj = {
+ warmupTime: 10,
+ };
+ }
+ return { ...item, key: item.id, warmupTime: propsObj.warmupTime };
});
this.setState({
popup: (
<ProxySelectorModal
recordCount={updateArray.length}
discoveryUpstreams={updateArray}
- tcpType={data.discovery.type}
+ discoveryType={data.discovery.type}
typeEnums={typeEnums}
isAdd={false}
isSetConfig={isSetConfig}
@@ -368,6 +376,16 @@ export default class TCPProxy extends Component {
discoveryDicts={discoveryDics}
handleOk={values => {
const {name, forwardPort, props, listenerNode, handler,
discoveryProps, serverList, upstreams} = values;
+ const upstreamsWithProps = upstreams.map(item => ({
+ protocol: item.protocol,
+ url: item.url,
+ status: parseInt(item.status, 10),
+ weight: item.weight,
+ startupTime: item.startupTime,
+ props: JSON.stringify({
+ warmupTime: item.warmupTime
+ })
+ }));
dispatch({
type: 'discovery/update',
payload: {
@@ -384,7 +402,7 @@ export default class TCPProxy extends Component {
serverList,
props: discoveryProps
},
- discoveryUpstreams: upstreams
+ discoveryUpstreams: upstreamsWithProps
},
callback: () => {
this.closeUpdateModal();
@@ -446,14 +464,14 @@ export default class TCPProxy extends Component {
render() {
const {popup} = this.state;
- const {selectorList, totalPage, currentPage, pageSize} = this.props;
+ const {proxySelectorList, totalPage, currentPage, pageSize} = this.props;
const tag = {
text: this.state.isPluginEnabled ? getIntlContent("SHENYU.COMMON.OPEN")
: getIntlContent("SHENYU.COMMON.CLOSE"),
color: this.state.isPluginEnabled ? 'green' : 'red'
}
return (
<>
- <div className={tcpStyles.main}>
+ <div className={discoveryStyles.main}>
<Row style={{marginBottom: '0px', display: 'flex', justifyContent:
'space-between', alignItems: 'center'}}>
<div style={{display: 'flex', alignItems: 'end', flex: 1, margin:
0}}>
<Title level={2} style={{textTransform: 'capitalize', margin: '0
20px 0 0'}}>
@@ -476,7 +494,7 @@ export default class TCPProxy extends Component {
</Row>
<Row>
- <div className={tcpStyles["header-bar"]}>
+ <div className={discoveryStyles["header-bar"]}>
<h3 style={{overflow: "visible", margin: 0}}>
{getIntlContent("SHENYU.PLUGIN.SELECTOR.LIST.TITLE")}
</h3>
@@ -529,7 +547,7 @@ export default class TCPProxy extends Component {
alignItems: 'stretch'
}}
>
- {this.renderCards(selectorList)}
+ {this.renderCards(proxySelectorList)}
</div>
</Row>
diff --git a/src/routes/Plugin/PluginRuleHandle/ParamPluginRuleHandle.js
b/src/routes/Plugin/PluginRuleHandle/ParamPluginRuleHandle.js
index 7728ac87..2aa8af2d 100644
--- a/src/routes/Plugin/PluginRuleHandle/ParamPluginRuleHandle.js
+++ b/src/routes/Plugin/PluginRuleHandle/ParamPluginRuleHandle.js
@@ -16,7 +16,7 @@
*/
import React, {Component} from "react";
-import {Form, Select, Row, Col, Input, Button, Tabs, Table} from "antd";
+import {Button, Col, Form, Input, Row, Select, Table, Tabs} from "antd";
import {getIntlContent} from '../../../utils/IntlUtils'
const {Option} = Select;
@@ -96,7 +96,6 @@ class ParamPluginRuleConfig extends Component {
const data = {};
const currentData = this.getCurrentData();
const valueStr = JSON.stringify(currentData);
- // console.log(currentData);
if (value !== undefined) {
try {
Object.assign(data, JSON.parse(value));
@@ -315,8 +314,7 @@ export default class ParamPluginRuleHandle extends
Component {
const {
form: {getFieldValue}
} = this.props;
- const value = getFieldValue("handle");
- return value;
+ return getFieldValue("handle");
};
render() {
diff --git a/src/services/api.js b/src/services/api.js
index 28bb80ca..e6b3ee36 100644
--- a/src/services/api.js
+++ b/src/services/api.js
@@ -1081,3 +1081,22 @@ export function fetchAlertReport(params) {
}
});
}
+
+export function bindingSelector(params) {
+ return request(`${baseUrl}/proxy-selector/binding`,
+ {
+ method: `POST`,
+ body: params
+ });
+}
+
+export function updateDiscoveryUpstream(discoveryHandlerId, upstreams) {
+ return request(`${baseUrl}/discovery-upstream/${discoveryHandlerId}`,
+ {
+ method: `PUT`,
+ body: upstreams
+ });
+}
+
+
+
diff --git a/src/utils/AuthRoute.js b/src/utils/AuthRoute.js
index 50b9ff9c..9380412a 100644
--- a/src/utils/AuthRoute.js
+++ b/src/utils/AuthRoute.js
@@ -241,7 +241,6 @@ const setMenuIconAndSort = (menus, permissions) => {
if (Array.isArray(menuArr)) {
menuArr.forEach(menu => {
const currentMenu = iconAndSortMap[formatRouteUrl(menu.path)];
- // console.log(menu, currentMenu, menu.path);
if (
currentMenu &&
currentMenu.meta.icon &&