blob: e924afc5f63f68c21d236eee8e0ba194d0a8cd25 [file] [log] [blame]
/*
* 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 {
Button,
Form,
Input,
Modal,
notification,
Select,
Space,
Switch,
Table,
Typography,
} from 'antd';
import React, { useState } from 'react';
import { useIntl } from 'umi';
import PanelSection from '@/components/PanelSection';
const { Title, Text } = Typography;
const MatchingRulesView: React.FC<RouteModule.Step1PassProps> = ({
advancedMatchingRules,
disabled,
onChange = () => {},
}) => {
const [visible, setVisible] = useState(false);
const [mode, setMode] = useState<RouteModule.ModalType>('CREATE');
const [namePlaceholder, setNamePlaceholder] = useState('');
const [modalForm] = Form.useForm();
const [operatorValueSample, setOperatorValueSample] = useState('');
const { Option } = Select;
const { formatMessage } = useIntl();
const onOk = () => {
modalForm.validateFields().then((value: RouteModule.MatchingRule) => {
if (value.operator === 'IN') {
try {
JSON.parse(value.value as string);
} catch (error) {
notification.warning({
message: formatMessage({ id: 'page.route.fields.vars.invalid' }),
description: formatMessage({ id: 'page.route.fields.vars.in.invalid' }),
});
return;
}
}
if (mode === 'EDIT') {
const key = modalForm.getFieldValue('key');
onChange({
action: 'advancedMatchingRulesChange',
data: advancedMatchingRules.map((rule) => {
if (rule.key === key) {
return { ...value, key };
}
return rule;
}),
});
} else {
const rule = {
...value,
key: Math.random().toString(36).slice(2),
};
onChange({
action: 'advancedMatchingRulesChange',
data: advancedMatchingRules.concat(rule),
});
}
modalForm.resetFields();
setVisible(false);
});
};
const handleEdit = (record: RouteModule.MatchingRule) => {
setMode('EDIT');
setVisible(true);
modalForm.setFieldsValue(record);
};
const handleRemove = (key: string) => {
onChange({
action: 'advancedMatchingRulesChange',
data: advancedMatchingRules.filter((item) => item.key !== key),
});
};
const OperatorRenderText = {
'==': formatMessage({ id: 'page.route.equal' }),
'~=': formatMessage({ id: 'page.route.unequal' }),
'>': formatMessage({ id: 'page.route.greaterThan' }),
'<': formatMessage({ id: 'page.route.lessThan' }),
'~~': formatMessage({ id: 'page.route.regexMatch' }),
'~*': formatMessage({ id: 'page.route.caseInsensitiveRegexMatch' }),
IN: formatMessage({ id: 'page.route.in' }),
HAS: formatMessage({ id: 'page.route.has' }),
};
const columns = [
{
title: formatMessage({ id: 'page.route.parameterPosition' }),
key: 'position',
render: (text: RouteModule.MatchingRule) => {
let renderText;
switch (text.position) {
case 'http':
renderText = formatMessage({ id: 'page.route.httpRequestHeader' });
break;
case 'arg':
renderText = formatMessage({ id: 'page.route.requestParameter' });
break;
case 'post_arg':
renderText = formatMessage({ id: 'page.route.postRequestParameter' });
break;
case 'cookie':
renderText = 'Cookie';
break;
case 'buildin':
renderText = formatMessage({ id: 'page.route.builtinParameter' });
break;
default:
renderText = '';
}
return renderText;
},
},
{
title: formatMessage({ id: 'page.route.parameterName' }),
dataIndex: 'name',
key: 'name',
},
{
title: formatMessage({ id: 'page.route.reverse' }),
key: 'reverse',
render: (text: RouteModule.MatchingRule) => text.reverse.toString(),
},
{
title: formatMessage({ id: 'page.route.operationalCharacter' }),
key: 'operator',
render: (text: RouteModule.MatchingRule) => OperatorRenderText[text.operator],
},
{
title: formatMessage({ id: 'page.route.value' }),
dataIndex: 'value',
key: 'value',
},
disabled
? {}
: {
title: formatMessage({ id: 'component.global.operation' }),
key: 'action',
render: (_: any, record: RouteModule.MatchingRule) => (
<Space size="middle">
<a onClick={() => handleEdit(record)}>
{formatMessage({ id: 'component.global.edit' })}
</a>
<a onClick={() => handleRemove(record.key)}>
{formatMessage({ id: 'component.global.delete' })}
</a>
</Space>
),
},
].filter((item) => Object.keys(item).length);
const renderModal = () => {
return (
<Modal
title={
mode === 'EDIT'
? formatMessage({ id: 'page.route.rule.edit' })
: formatMessage({ id: 'page.route.rule.create' })
}
centered
visible
onOk={onOk}
onCancel={() => {
setVisible(false);
modalForm.resetFields();
}}
okText={formatMessage({ id: 'component.global.confirm' })}
cancelText={formatMessage({ id: 'component.global.cancel' })}
maskClosable={false}
destroyOnClose
>
<Form form={modalForm} layout="vertical">
<Form.Item
label={formatMessage({ id: 'page.route.parameterPosition' })}
name="position"
rules={[
{
required: true,
message: `${formatMessage({ id: 'component.global.pleaseEnter' })} ${formatMessage({
id: 'page.route.parameterPosition',
})}`,
},
]}
>
<Select
onChange={(value) => {
if (value === 'http') {
setNamePlaceholder(
formatMessage({ id: 'page.route.input.placeholder.parameterNameHttpHeader' }),
);
} else {
setNamePlaceholder(
formatMessage({
id: 'page.route.input.placeholder.parameterNameRequestParameter',
}),
);
}
}}
>
<Option value="http">{formatMessage({ id: 'page.route.httpRequestHeader' })}</Option>
<Option value="arg">{formatMessage({ id: 'page.route.requestParameter' })}</Option>
<Option value="post_arg">
{formatMessage({ id: 'page.route.postRequestParameter' })}
</Option>
<Option value="cookie">Cookie</Option>
<Option value="buildin">
{formatMessage({ id: 'page.route.builtinParameter' })}
</Option>
</Select>
</Form.Item>
<Form.Item
label={formatMessage({ id: 'page.route.parameterName' })}
name="name"
rules={[
{
required: true,
message: `${formatMessage({ id: 'component.global.pleaseEnter' })} ${formatMessage({
id: 'page.route.parameterName',
})}`,
},
{
pattern: new RegExp(/^([a-zA-Z][a-zA-Z0-9_-]*$)/, 'g'),
message: formatMessage({ id: 'component.global.input.ruleMessage.name' }),
},
]}
tooltip={formatMessage({
id: 'page.route.form.itemRulesRequiredMessage.parameterName',
})}
extra={namePlaceholder}
>
<Input />
</Form.Item>
<Form.Item
label={formatMessage({ id: 'page.route.reverse' })}
name={'reverse'}
valuePropName={'checked'}
required
initialValue={false}
>
<Switch />
</Form.Item>
<Form.Item
label={formatMessage({ id: 'page.route.operationalCharacter' })}
name="operator"
rules={[
{
required: true,
message: `${formatMessage({ id: 'component.global.pleaseChoose' })} ${formatMessage(
{
id: 'page.route.operationalCharacter',
},
)}`,
},
]}
>
<Select
onChange={(e: string) => {
switch (e) {
case 'IN':
setOperatorValueSample(
formatMessage({ id: 'page.route.advanced-match.operator.sample.IN' }),
);
break;
case '~~':
case '~*':
setOperatorValueSample(
formatMessage({ id: 'page.route.advanced-match.operator.sample.~~' }),
);
break;
default:
setOperatorValueSample('');
}
}}
>
{Object.keys(OperatorRenderText).map((item) => (
<Option value={item} key={item}>
{OperatorRenderText[item]}
</Option>
))}
</Select>
</Form.Item>
<Form.Item
label={formatMessage({ id: 'page.route.value' })}
name="value"
rules={[
{
required: true,
message: `${formatMessage({ id: 'component.global.pleaseEnter' })} ${formatMessage({
id: 'page.route.value',
})}`,
},
]}
extra={operatorValueSample}
>
<Input />
</Form.Item>
<Typography>
<Title level={5}>{formatMessage({ id: 'page.route.advanced-match.message' })}</Title>
<Text>
{formatMessage({ id: 'page.route.advanced-match.tips.requestParameter' })}
<br />
{formatMessage({ id: 'page.route.advanced-match.tips.postRequestParameter' })}
<br />
{formatMessage({ id: 'page.route.advanced-match.tips.builtinParameter' })}
</Text>
</Typography>
</Form>
</Modal>
);
};
return (
<PanelSection
title={formatMessage({ id: 'page.route.panelSection.title.advancedMatchRule' })}
tooltip={formatMessage({ id: 'page.route.advanced-match.tooltip' })}
>
{!disabled && (
<Button
onClick={() => {
setMode('CREATE');
setVisible(true);
}}
type="primary"
style={{
marginBottom: 16,
}}
>
{formatMessage({ id: 'component.global.add' })}
</Button>
)}
<Table key="table" bordered dataSource={advancedMatchingRules} columns={columns} />
{/* NOTE: tricky way, switch visible on Modal component will occur error */}
{visible ? renderModal() : null}
</PanelSection>
);
};
export default MatchingRulesView;