Skip to content

Commit 4e72d92

Browse files
authored
fix: ! (reverse) operator not handled correctly (#2364)
1 parent 561ed37 commit 4e72d92

File tree

4 files changed

+59
-13
lines changed

4 files changed

+59
-13
lines changed

web/cypress/integration/route/create-route-with-advanced-matching-conditions.spec.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ context('Create Route with advanced matching conditions', () => {
2626
notification: '.ant-notification-notice-message',
2727
parameterPosition: '#position',
2828
ruleCard: '.ant-modal',
29+
reverse: '#reverse',
2930
operator: '#operator',
3031
value: '#value',
3132
advancedMatchingTable: '.ant-table-row.ant-table-row-level-0',
@@ -84,6 +85,14 @@ context('Create Route with advanced matching conditions', () => {
8485
cy.get(selector.ruleCard).within(() => {
8586
cy.get(selector.name).type(data.matchingParamName);
8687
});
88+
// reverse switch should exist
89+
cy.get(selector.reverse)
90+
.should('exist')
91+
.and('be.visible')
92+
.should('have.class', 'ant-switch')
93+
.and('not.have.class', 'ant-switch-checked')
94+
.click()
95+
.and('have.class', 'ant-switch-checked');
8796
cy.get(selector.operator).click();
8897
cy.get(`[title="${opreator}"]`).should('be.visible').click();
8998
cy.get(selector.value).type(matchingValueList1[index]);
@@ -93,6 +102,7 @@ context('Create Route with advanced matching conditions', () => {
93102
cy.get(selector.advancedMatchingTable).should('exist');
94103
cy.wrap(opreatorList).each((operator, index) => {
95104
cy.get(selector.advancedMatchingTableCell).within(() => {
105+
cy.contains('th', 'Reverse the result(!)').should('be.visible');
96106
cy.contains('td', 'Built-in Parameter').should('be.visible');
97107
cy.contains('td', data.matchingParamName).should('be.visible');
98108
cy.contains('td', matchingValueList1[index]).should('be.visible');
@@ -129,11 +139,13 @@ context('Create Route with advanced matching conditions', () => {
129139
cy.get(selector.ruleCard).within(() => {
130140
cy.get(`[title="Built-in Parameter"]`).should('have.class', 'ant-select-selection-item');
131141
cy.get(selector.name).clear().type(data.matchingParamName);
142+
cy.get(selector.reverse).should('have.class', 'ant-switch-checked');
132143
cy.get(`[title="${opreator}"]`).should('have.class', 'ant-select-selection-item');
133144
cy.get(selector.value).clear().type(matchingValueList2[index]);
134145
cy.contains('Confirm').click();
135146
});
136147
cy.get(selector.advancedMatchingTableCell).within(() => {
148+
cy.contains('th', 'Reverse the result(!)').should('be.visible');
137149
cy.contains('td', 'Built-in Parameter').should('be.visible');
138150
cy.contains('td', data.matchingParamName).should('be.visible');
139151
cy.contains('td', matchingValueList2[index]).should('be.visible');

web/src/pages/Route/components/Step1/MatchingRulesView.tsx

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,18 @@
1515
* limitations under the License.
1616
*/
1717
import React, { useState } from 'react';
18-
import { Button, Table, Modal, Form, Select, Input, Space, notification, Typography } from 'antd';
18+
import {
19+
Button,
20+
Table,
21+
Modal,
22+
Form,
23+
Select,
24+
Input,
25+
Space,
26+
notification,
27+
Typography,
28+
Switch,
29+
} from 'antd';
1930
import { useIntl } from 'umi';
2031

2132
import PanelSection from '@/components/PanelSection';
@@ -99,7 +110,6 @@ const MatchingRulesView: React.FC<RouteModule.Step1PassProps> = ({
99110
'~*': formatMessage({ id: 'page.route.caseInsensitiveRegexMatch' }),
100111
IN: formatMessage({ id: 'page.route.in' }),
101112
HAS: formatMessage({ id: 'page.route.has' }),
102-
'!': formatMessage({ id: 'page.route.reverse' }),
103113
};
104114

105115
const columns = [
@@ -135,6 +145,11 @@ const MatchingRulesView: React.FC<RouteModule.Step1PassProps> = ({
135145
dataIndex: 'name',
136146
key: 'name',
137147
},
148+
{
149+
title: formatMessage({ id: 'page.route.reverse' }),
150+
key: 'reverse',
151+
render: (text: RouteModule.MatchingRule) => text.reverse.toString(),
152+
},
138153
{
139154
title: formatMessage({ id: 'page.route.operationalCharacter' }),
140155
key: 'operator',
@@ -244,6 +259,14 @@ const MatchingRulesView: React.FC<RouteModule.Step1PassProps> = ({
244259
>
245260
<Input />
246261
</Form.Item>
262+
<Form.Item
263+
label={formatMessage({ id: 'page.route.reverse' })}
264+
name={'reverse'}
265+
valuePropName={'checked'}
266+
required
267+
>
268+
<Switch />
269+
</Form.Item>
247270
<Form.Item
248271
label={formatMessage({ id: 'page.route.operationalCharacter' })}
249272
name="operator"

web/src/pages/Route/transform.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ export const transformStepData = ({
153153
labels,
154154
...step3DataCloned,
155155
vars: advancedMatchingRules.map((rule) => {
156-
const { operator, position, name, value } = rule;
157-
let key = '';
156+
const { reverse, operator, position, name, value } = rule;
157+
let key: string;
158158
switch (position) {
159159
case 'cookie':
160160
key = `cookie_${name}`;
@@ -175,7 +175,7 @@ export const transformStepData = ({
175175
if (operator === 'IN') {
176176
finalValue = JSON.parse(value as string);
177177
}
178-
return [key, operator, finalValue];
178+
return reverse ? [key, '!', operator, finalValue] : [key, operator, finalValue];
179179
}),
180180
// @ts-ignore
181181
methods: form1Data.methods.includes('ALL') ? [] : form1Data.methods,
@@ -274,12 +274,15 @@ export const transformStepData = ({
274274
]);
275275
};
276276

277-
const transformVarsToRules = (
278-
data: [string, RouteModule.Operator, string | any[]][] = [],
279-
): RouteModule.MatchingRule[] =>
280-
data.map(([key, operator, value]) => {
281-
let position = '';
282-
let name = '';
277+
const transformVarsToRules = (data: RouteModule.VarTuple[] = []): RouteModule.MatchingRule[] =>
278+
data.map((varTuple) => {
279+
const key = varTuple[0];
280+
const reverse = varTuple[1] === '!';
281+
const operator = varTuple[1] === '!' ? varTuple[2] : varTuple[1];
282+
const value = varTuple[varTuple.length - 1];
283+
284+
let position: string;
285+
let name: string;
283286
const regex = new RegExp('^(cookie|http|arg|post_arg)_.+');
284287
if (regex.test(key)) {
285288
[, position, name] = key.split(/^(cookie|http|arg|post_arg)_/);
@@ -291,6 +294,7 @@ const transformVarsToRules = (
291294
position: position as RouteModule.VarPosition,
292295
name,
293296
value: typeof value === 'object' ? JSON.stringify(value) : value,
297+
reverse,
294298
operator,
295299
key: Math.random().toString(36).slice(2),
296300
};

web/src/pages/Route/typing.d.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@
1515
* limitations under the License.
1616
*/
1717
declare namespace RouteModule {
18-
type Operator = '==' | '~=' | '>' | '<' | '~~' | '~*' | 'IN' | 'HAS' | '!';
18+
type OperatorNot = '!';
19+
20+
type Operator = '==' | '~=' | '>' | '<' | '~~' | '~*' | 'IN' | 'HAS';
21+
22+
type VarTuple =
23+
| [string, RouteModule.Operator, string | any[]]
24+
| [string, RouteModule.OperatorNot, RouteModule.Operator, string | any[]];
1925

2026
type VarPosition = 'arg' | 'post_arg' | 'http' | 'cookie' | 'buildin';
2127

@@ -75,7 +81,7 @@ declare namespace RouteModule {
7581
remote_addr?: string;
7682
remote_addrs?: string[];
7783
upstream: UpstreamComponent.ResponseData;
78-
vars: [string, Operator, string | any[]][];
84+
vars: VarTuple[];
7985
upstream_path?: {
8086
type?: string;
8187
from?: string;
@@ -93,6 +99,7 @@ declare namespace RouteModule {
9399
type MatchingRule = {
94100
position: VarPosition;
95101
name: string;
102+
reverse: boolean;
96103
operator: Operator;
97104
value: string | any[];
98105
key: string;

0 commit comments

Comments
 (0)