| /* |
| * 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; |