Merge branch 'develop' of https://github.com/alibaba/nacos into develop

This commit is contained in:
xiaochun.xxc 2019-07-04 13:53:31 +08:00
commit 024cf2a1c3
12 changed files with 679 additions and 19 deletions

View File

@ -19,8 +19,7 @@ matrix:
env: CUSTOM_JDK="oraclejdk8" env: CUSTOM_JDK="oraclejdk8"
jdk: jdk:
- openjdk10 - openjdk11
- openjdk9
- openjdk8 - openjdk8
before_install: before_install:

View File

@ -17,9 +17,9 @@ package com.alibaba.nacos.client.config.utils;
import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.common.Constants;
import com.google.common.collect.Maps;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
@ -33,7 +33,7 @@ public class MD5 {
private static int DIGITS_SIZE = 16; private static int DIGITS_SIZE = 16;
private static char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; private static char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
private static Map<Character, Integer> rDigits = new HashMap<Character, Integer>(16); private static Map<Character, Integer> rDigits = Maps.newHashMapWithExpectedSize(16);
static { static {
for (int i = 0; i < digits.length; ++i) { for (int i = 0; i < digits.length; ++i) {

View File

@ -217,6 +217,7 @@ public class NamingProxy {
params.put("ip", instance.getIp()); params.put("ip", instance.getIp());
params.put("port", String.valueOf(instance.getPort())); params.put("port", String.valueOf(instance.getPort()));
params.put("weight", String.valueOf(instance.getWeight())); params.put("weight", String.valueOf(instance.getWeight()));
params.put("enabled", String.valueOf(instance.isEnabled()));
params.put("ephemeral", String.valueOf(instance.isEphemeral())); params.put("ephemeral", String.valueOf(instance.isEphemeral()));
params.put("metadata", JSON.toJSONString(instance.getMetadata())); params.put("metadata", JSON.toJSONString(instance.getMetadata()));

View File

@ -351,9 +351,12 @@ const I18N_CONF = {
}, },
ConfigEditor: { ConfigEditor: {
official: 'Official', official: 'Official',
production: 'Production',
beta: 'BETA',
wrong: 'Error', wrong: 'Error',
submitFailed: 'Cannot be empty, submit failed', submitFailed: 'Cannot be empty, submit failed',
toedittitle: 'Edit Configuration', toedittitle: 'Edit Configuration',
newConfigEditor: 'New Config Editor',
toedit: 'Edit Configuration', toedit: 'Edit Configuration',
vdchart: 'Illegal characters not allowed', vdchart: 'Illegal characters not allowed',
recipientFrom: 'Data ID cannot be empty', recipientFrom: 'Data ID cannot be empty',
@ -369,8 +372,12 @@ const I18N_CONF = {
escExit: 'Press F1 to view in full screen', escExit: 'Press F1 to view in full screen',
releaseBeta: 'Press Esc to exit ', releaseBeta: 'Press Esc to exit ',
release: 'Beta Publish', release: 'Beta Publish',
stopPublishBeta: 'Stop Beta',
betaPublish: 'Beta Publish:',
betaSwitchPrompt: 'Not checked by default.',
publish: 'Publish', publish: 'Publish',
back: 'Back', back: 'Back',
codeValErrorPrompt: 'Configuration information may have syntax errors. Are you sure to submit?',
}, },
EditorNameSpace: { EditorNameSpace: {
notice: 'Notice', notice: 'Notice',

View File

@ -349,10 +349,13 @@ const I18N_CONF = {
}, },
ConfigEditor: { ConfigEditor: {
official: '正式', official: '正式',
production: '正式',
beta: 'BETA',
wrong: '错误', wrong: '错误',
submitFailed: '不能为空, 提交失败', submitFailed: '不能为空, 提交失败',
toedittitle: '编辑配置', toedittitle: '编辑配置',
toedit: '编辑配置', toedit: '编辑配置',
newConfigEditor: '新建配置',
vdchart: '请勿输入非法字符', vdchart: '请勿输入非法字符',
recipientFrom: 'Data ID不能为空', recipientFrom: 'Data ID不能为空',
homeApplication: 'Group不能为空', homeApplication: 'Group不能为空',
@ -360,15 +363,19 @@ const I18N_CONF = {
groupNotEmpty: '更多高级选项', groupNotEmpty: '更多高级选项',
tags: '标签:', tags: '标签:',
pleaseEnterTag: '请输入标签', pleaseEnterTag: '请输入标签',
targetEnvironment: '归属应用', targetEnvironment: '归属应用:',
description: '描述:', description: '描述:',
format: '配置格式', format: '配置格式:',
configcontent: '配置内容', configcontent: '配置内容',
escExit: '按F1显示全屏', escExit: '按F1显示全屏',
releaseBeta: '按Esc退出全屏', releaseBeta: '按Esc退出全屏',
release: '发布Beta', release: '发布Beta',
stopPublishBeta: '停止Beta',
betaPublish: 'Beta发布:',
betaSwitchPrompt: '默认不要勾选',
publish: '发布', publish: '发布',
back: '返回', back: '返回',
codeValErrorPrompt: '配置信息可能有语法错误, 确定提交吗?',
}, },
EditorNameSpace: { EditorNameSpace: {
notice: '提示', notice: '提示',

View File

@ -0,0 +1,572 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
* Licensed 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 from 'react';
import PropTypes from 'prop-types';
import { getParams } from '../../../globalLib';
import request from '../../../utils/request';
import validateContent from 'utils/validateContent';
import SuccessDialog from '../../../components/SuccessDialog';
import DiffEditorDialog from '../../../components/DiffEditorDialog';
import './index.scss';
import {
Balloon,
Button,
Dialog,
Field,
Form,
Checkbox,
Icon,
Input,
Loading,
Radio,
Switch,
Select,
Tab,
Message,
Grid,
ConfigProvider,
} from '@alifd/next';
const { Row, Col } = Grid;
const LANGUAGE_LIST = [
{ value: 'text', label: 'TEXT' },
{ value: 'json', label: 'JSON' },
{ value: 'xml', label: 'XML' },
{ value: 'yaml', label: 'YAML' },
{ value: 'html', label: 'HTML' },
{ value: 'properties', label: 'Properties' },
];
const TAB_LIST = ['production', 'beta'];
@ConfigProvider.config
class ConfigEditor extends React.Component {
static displayName = 'ConfigEditor';
static propTypes = {
locale: PropTypes.object,
history: PropTypes.object,
};
constructor(props) {
super(props);
this.state = {
loading: false,
isBeta: false,
isNewConfig: true,
betaPublishSuccess: false,
betaIps: '',
tabActiveKey: '',
form: {
dataId: '', // 配置 ID
group: '', // 分组
content: '', // 配置内容
appName: '', // 应用名
desc: '', // 描述
config_tags: [],
type: 'text', // 配置格式
},
tagDataSource: [],
openAdvancedSettings: false,
};
this.successDialog = React.createRef();
this.diffEditorDialog = React.createRef();
}
componentDidMount() {
const isNewConfig = !getParams('dataId');
const group = getParams('group').trim();
this.setState({ isNewConfig }, () => {
if (!isNewConfig) {
this.changeForm(
{
dataId: getParams('dataId').trim(),
group,
},
() => this.getConfig()
);
} else {
if (group) {
this.setState({ group });
}
this.initMoacoEditor('text', '');
}
});
}
initMoacoEditor(language, value) {
const container = document.getElementById('container');
container.innerHTML = '';
this.monacoEditor = null;
const options = {
value,
language: this.state.configType,
codeLens: true,
selectOnLineNumbers: true,
roundedSelection: false,
readOnly: false,
lineNumbersMinChars: true,
theme: 'vs-dark',
wordWrapColumn: 120,
folding: false,
showFoldingControls: 'always',
wordWrap: 'wordWrapColumn',
cursorStyle: 'line',
automaticLayout: true,
};
if (!window.monaco) {
window.importEditor(() => {
this.monacoEditor = window.monaco.editor.create(container, options);
});
} else {
this.monacoEditor = window.monaco.editor.create(container, options);
}
}
createDiffCodeMirror(leftCode, rightCode) {
const target = this.diffEditorDialog.current.getInstance();
target.innerHTML = '';
this.diffeditor = window.CodeMirror.MergeView(target, {
value: leftCode || '',
origLeft: null,
orig: rightCode || '',
lineNumbers: true,
mode: this.mode,
theme: 'xq-light',
highlightDifferences: true,
connect: 'align',
collapseIdentical: false,
});
}
openDiff(cbName) {
this.diffcb = cbName;
let leftvalue = this.monacoEditor.getValue();
let rightvalue = this.codeVal;
leftvalue = leftvalue.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
rightvalue = rightvalue.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
this.diffEditorDialog.current.getInstance().openDialog(leftvalue, rightvalue);
}
clickTab(tabActiveKey) {
this.setState({ tabActiveKey }, () => this.getConfig(tabActiveKey === 'bata'));
}
getCodeVal() {
const { locale = {} } = this.props;
const { type, content } = this.state.form;
const codeVal = this.monacoEditor ? this.monacoEditor.getValue() : content;
if (!codeVal) {
Message.error({
content: locale.submitFailed,
align: 'cc cc',
});
return false;
}
return codeVal;
}
publish() {
const { locale = {} } = this.props;
const { type } = this.state.form;
if (this.state.isNewConfig) {
this.validation();
}
const content = this.getCodeVal();
if (!content) {
return;
}
if (validateContent.validate({ content, type })) {
return this._publishConfig();
} else {
Dialog.confirm({
content: locale.codeValErrorPrompt,
onOk: () => this._publishConfig(),
});
return false;
}
}
_publishConfig(beta = false) {
const { locale } = this.props;
const { betaIps, isNewConfig } = this.state;
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
if (beta) {
headers.betaIps = betaIps;
}
const data = { ...this.state.form, content: this.getCodeVal() };
return request({
url: 'v1/cs/configs',
method: 'post',
data,
transformRequest: [
function(data) {
let ret = '';
for (let it in data) {
ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&';
}
return ret;
},
],
headers,
}).then(res => {
if (res) {
if (isNewConfig) {
this.setState({ isNewConfig: false });
}
this.getConfig();
}
return res;
});
}
publishBeta() {
return this._publishConfig(true).then(res => {
if (res) {
this.setState(
{
betaPublishSuccess: true,
tabActiveKey: 'beta',
},
() => this.getConfig(true)
);
}
});
}
stopBeta() {
const { locale } = this.props;
const { dataId, group } = this.state.form;
return request
.delete('v1/cs/configs', {
params: {
beta: true,
dataId,
group,
},
})
.then(res => {
if (res.data) {
this.setState(
{
isBeta: false,
betaPublishSuccess: false,
tabActiveKey: '',
},
() => this.getConfig()
);
}
return res;
});
}
changeForm(item, cb) {
const { form } = this.state;
this.setState({ form: { ...form, ...item } }, () => {
if (cb) {
cb();
}
});
}
setConfigTags(tags) {
const { tagDataSource } = this.state;
const lastTag = tags[tags.length - 1];
if (tagDataSource.indexOf(lastTag) < 0) {
this.setState({ tagDataSource: [...tagDataSource, lastTag] });
}
if (tags.length > 5) {
tags.pop();
}
tags.forEach((v, i) => {
if (v.indexOf(',') !== -1 || v.indexOf('=') !== -1) {
tags.splice(i, 1);
}
});
this.changeForm({ config_tags: tags });
}
goBack() {
const serverId = getParams('serverId') || '';
const tenant = getParams('namespace');
const searchGroup = getParams('searchGroup') || '';
const searchDataId = getParams('searchDataId') || '';
this.props.history.push(
`/configurationManagement?serverId=${serverId}&group=${searchGroup}&dataId=${searchDataId}&namespace=${tenant}`
);
}
getConfig(beta = false) {
const namespace = getParams('namespace');
const { dataId, group } = this.state.form;
return request
.get('v1/cs/configs', {
params: {
dataId,
group,
beta,
show: 'all',
namespaceId: namespace,
tenant: namespace,
},
})
.then(res => {
const { type, content, configTags } = res;
this.changeForm({ ...res, config_tags: configTags ? configTags.split(',') : [] });
this.initMoacoEditor(type, content);
this.codeVal = content;
return res;
});
}
validation() {
const { locale } = this.props;
const { form } = this.state;
const { dataId, group } = form;
if (!dataId) {
this.setState({
dataIdError: {
validateState: 'error',
help: locale.recipientFrom,
},
});
return false;
}
if (!group) {
this.setState({
groupError: {
validateState: 'error',
help: locale.homeApplication,
},
});
return false;
}
return true;
}
render() {
const {
loading,
betaIps,
openAdvancedSettings,
isBeta,
isNewConfig,
betaPublishSuccess,
form,
tagDataSource,
tabActiveKey,
dataIdError = {},
groupError = {},
} = this.state;
const { locale = {} } = this.props;
return (
<div className="config-editor">
<Loading
shape="flower"
style={{ position: 'relative', width: '100%' }}
visible={loading}
tip="Loading..."
color="#333"
>
<h1 className="func-title">
<div>{locale.toedit}</div>
</h1>
{betaPublishSuccess && (
<Tab shape="wrapped" activeKey={tabActiveKey} onChange={key => this.clickTab(key)}>
{TAB_LIST.map(key => (
<Tab.Item title={locale[key]} key={key}>
{locale[key]}
</Tab.Item>
))}
</Tab>
)}
<Form className="form">
<Form.Item label="Data ID:" required {...dataIdError}>
<Input
value={form.dataId}
onChange={dataId =>
this.changeForm({ dataId }, () => this.setState({ dataIdError: {} }))
}
disabled={!isNewConfig}
/>
</Form.Item>
<Form.Item label="Group:" required {...groupError}>
<Input
value={form.group}
onChange={group =>
this.changeForm({ group }, () => this.setState({ groupError: {} }))
}
disabled={!isNewConfig}
/>
</Form.Item>
<Form.Item label=" ">
<div
className="switch"
onClick={() => this.setState({ openAdvancedSettings: !openAdvancedSettings })}
>
{openAdvancedSettings ? locale.collapse : locale.groupNotEmpty}
</div>
</Form.Item>
{openAdvancedSettings && (
<>
<Form.Item label={locale.tags}>
<Select
size="medium"
hasArrow
autoWidth
mode="tag"
filterLocal
value={form.config_tags}
dataSource={tagDataSource}
onChange={config_tags => this.setConfigTags(config_tags)}
hasClear
/>
</Form.Item>
<Form.Item label={locale.targetEnvironment}>
<Input value={form.appName} onChange={appName => this.changeForm({ appName })} />
</Form.Item>
</>
)}
<Form.Item label={locale.description}>
<Input.TextArea
value={form.desc}
aria-label="TextArea"
onChange={desc => this.changeForm({ desc })}
/>
</Form.Item>
{!isNewConfig && tabActiveKey !== 'production' && (
<Form.Item label={locale.betaPublish}>
{!betaPublishSuccess && (
<Checkbox checked={isBeta} onChange={isBeta => this.setState({ isBeta })}>
{locale.betaSwitchPrompt}
</Checkbox>
)}
{isBeta && (
<Input.TextArea
aria-label="TextArea"
placeholder="127.0.0.1,127.0.0.2"
onChange={betaIps => this.setState({ betaIps })}
/>
)}
</Form.Item>
)}
<Form.Item label={locale.format}>
<Radio.Group
value={form.type}
onChange={type => {
this.initMoacoEditor(type, '');
this.changeForm({ type });
}}
>
{LANGUAGE_LIST.map(item => (
<Radio value={item.value} key={item.value}>
{item.label}
</Radio>
))}
</Radio.Group>
</Form.Item>
<Form.Item
label={
<div className="help-label">
<span>{locale.configcontent}</span>
<Balloon
trigger={<Icon type="help" size="small" />}
align="t"
style={{ marginRight: 5 }}
triggerType="hover"
>
<p>{locale.escExit}</p>
<p>{locale.releaseBeta}</p>
</Balloon>
<span>:</span>
</div>
}
>
<div style={{ clear: 'both', height: 300 }} id="container" />
</Form.Item>
</Form>
<Row>
<Col span="24" className="button-list">
{isBeta && betaPublishSuccess && tabActiveKey !== 'production' && (
<Button
size="large"
type="primary"
onClick={() =>
this.stopBeta().then(() => {
this.successDialog.current.getInstance().openDialog({
title: <div>{locale.stopPublishBeta}</div>,
isok: true,
...form,
});
})
}
>
{locale.stopPublishBeta}
</Button>
)}
{isBeta && tabActiveKey !== 'production' && (
<Button
size="large"
type="primary"
disabled={!betaIps}
onClick={() => this.openDiff('publishBeta')}
>
{locale.release}
</Button>
)}
<Button
size="large"
type="primary"
disabled={tabActiveKey === 'production'}
onClick={() => this.openDiff('publish')}
>
{locale.publish}
</Button>
<Button size="large" type="normal" onClick={() => this.goBack()}>
{locale.back}
</Button>
</Col>
</Row>
<DiffEditorDialog
ref={this.diffEditorDialog}
publishConfig={() => {
this[this.diffcb]();
let title = locale.toedit;
if (isNewConfig) {
title = locale.newConfigEditor;
}
if (this.diffcb === 'publishBeta') {
title = locale.betaPublish;
}
if (this.diffcb === 'publish' && tabActiveKey === 'beta') {
title = locale.stopPublishBeta;
this.stopBeta();
}
this.successDialog.current.getInstance().openDialog({
title: <div>{title}</div>,
isok: true,
...form,
});
}}
/>
<SuccessDialog ref={this.successDialog} />
</Loading>
</div>
);
}
}
export default ConfigEditor;

View File

@ -11,6 +11,6 @@
* limitations under the License. * limitations under the License.
*/ */
import ConfigEditor from './ConfigEditor'; import ConfigEditor from './NewConfigEditor';
export default ConfigEditor; export default ConfigEditor;

View File

@ -10,3 +10,64 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
.config-editor {
padding: 10px;
.func-title {
overflow: hidden;
height: 50px;
width: 100%;
font-weight: 500;
margin-bottom: 9px;
font-size: 18px;
line-height: 36px;
color: #73777a;
}
.form {
display: table;
.next-form-item {
display: table-row;
.next-form-item-label {
white-space: nowrap;
word-break: keep-all;
}
.next-form-item-control,
.next-select {
width: 100%;
}
.next-form-item-label,
.next-form-item-control {
display: table-cell;
}
}
.next-form-item-control {
padding-bottom: 12px;
}
.next-checkbox-label {
color: #73777a;
font-weight: normal;
}
.next-radio-label {
color: #73777a;
}
.switch {
color: #33cde5;
cursor: pointer;
user-select: none;
}
.help-label > * {
display: inline-block;
}
.help-label > i {
color: #1dc11d;
margin: 0 0.25em;
}
}
.button-list {
text-align: right;
button {
margin-left: 1em;
font-size: 14px;
}
}
}

View File

@ -866,7 +866,6 @@ class ConfigurationManagement extends React.Component {
processImportAndCloneResult(ret, locale, confirm, isImport) { processImportAndCloneResult(ret, locale, confirm, isImport) {
const resultCode = ret.code; const resultCode = ret.code;
console.log(ret);
if (resultCode === 200) { if (resultCode === 200) {
confirm.hide(); confirm.hide();
if (ret.data.failData && ret.data.failData.length > 0) { if (ret.data.failData && ret.data.failData.length > 0) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -102,6 +102,20 @@ public class NamingMaintainService_ITCase {
System.out.println(instances.get(0)); System.out.println(instances.get(0));
} }
@Test
public void updateInstanceWithDisable() throws NacosException {
Map<String, String> map = new HashMap<String, String>();
map.put("netType", "external-update");
map.put("version", "2.0");
namingService.registerInstance(serviceName, instance);
instance.setMetadata(map);
instance.setEnabled(false);
namingMaintainService.updateInstance(serviceName, instance);
List<Instance> instances = namingService.getAllInstances(serviceName, true);
Assert.assertEquals(instances.size(), 0);
}
@Test @Test
public void createAndUpdateService() throws NacosException { public void createAndUpdateService() throws NacosException {