diff --git a/web/cypress/integration/consumer/create_and_delete_consumer.spec.js b/web/cypress/integration/consumer/create_and_delete_consumer.spec.js index 81cf2c9cb6..ba9ec3c105 100644 --- a/web/cypress/integration/consumer/create_and_delete_consumer.spec.js +++ b/web/cypress/integration/consumer/create_and_delete_consumer.spec.js @@ -29,7 +29,7 @@ context('Create and Delete Consumer', () => { serviceSelector: '[title=test_service]', monacoScroll: '.monaco-scrollable-element', monacoViewZones: '.view-zones', - notificationCloseIcon: '.ant-notification-close-icon' + notificationCloseIcon: '.ant-notification-close-icon', }; const data = { diff --git a/web/cypress/integration/route/create-edit-duplicate-delete-route.spec.js b/web/cypress/integration/route/create-edit-duplicate-delete-route.spec.js index c93ea9f87d..9031b9abb8 100644 --- a/web/cypress/integration/route/create-edit-duplicate-delete-route.spec.js +++ b/web/cypress/integration/route/create-edit-duplicate-delete-route.spec.js @@ -204,6 +204,9 @@ context('Create and Delete Route', () => { cy.get(selector.monacoScroll).within(() => { cy.contains('upstream').should('exist'); cy.contains('vars').should('exist'); + cy.contains('uri').should('exist'); + cy.contains('hosts').should('exist'); + cy.contains('remote_addr').should('exist'); cy.contains(name).should('exist'); }); }); @@ -220,6 +223,7 @@ context('Create and Delete Route', () => { cy.get('#status').should('have.class', 'ant-switch-checked'); cy.get(selector.name).clear().type(newName); cy.get(selector.description).clear().type(data.description2); + cy.get(selector.advancedMatchingTable).should('exist'); cy.wrap(opreatorList).each(() => { cy.get(selector.advancedMatchingTableOperation).within(() => { diff --git a/web/cypress/integration/route/create-route-both-use-uri-uris.spec.js b/web/cypress/integration/route/create-route-both-use-uri-uris.spec.js new file mode 100644 index 0000000000..34ccac3cbe --- /dev/null +++ b/web/cypress/integration/route/create-route-both-use-uri-uris.spec.js @@ -0,0 +1,193 @@ +/* + * 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. + */ +/* eslint-disable no-undef */ + +context('Create Route Both use uri and uris', () => { + const selector = { + empty: '.ant-empty-normal', + name: '#name', + description: '#desc', + hosts_0: '#hosts_0', + hosts_1: '#hosts_1', + uris_0: '#uris_0', + uris_1: '#uris_1', + remote_addrs_0: '#remote_addrs_0', + remote_addrs_1: '#remote_addrs_1', + nodes_0_host: '#nodes_0_host', + nodes_0_port: '#nodes_0_port', + nodes_0_weight: '#nodes_0_weight', + nameSelector: '[title=Name]', + drawer: '.ant-drawer-content', + monacoScroll: '.monaco-scrollable-element', + addHost: '[data-cy=addHost]', + addUri: '[data-cy=addUri]', + addRemoteAddr: '[data-cy=addRemoteAddr]', + deleteAlert: '.ant-modal-body', + notificationCloseIcon: '.ant-notification-close-icon', + notification: '.ant-notification-notice-message', + }; + + const data = { + name: 'test_route', + description: 'desc_by_autotest', + host: '12.12.12.12', + port: '80', + weight: 1, + host_0: '0.0.0.0', + uri_0: '/test', + remote_addr_0: '192.168.1.0', + host_1: '1.1.1.1', + uri_1: '/test1', + remote_addr_1: '192.168.1.1', + submitSuccess: 'Submit Successfully', + deleteRouteSuccess: 'Delete Route Successfully', + }; + + beforeEach(() => { + cy.login(); + }); + + it('should create route with uri/host/remote_addr', () => { + cy.visit('/'); + cy.contains('Route').click(); + cy.get(selector.empty).should('be.visible'); + cy.contains('Create').click(); + cy.contains('Next').click().click(); + cy.get(selector.name).type(data.name); + cy.get(selector.description).type(data.description); + + cy.get(selector.hosts_0).type(data.host_0); + cy.get(selector.uris_0).clear().type(data.uri_0); + cy.get(selector.remote_addrs_0).type(data.remote_addr_0); + + cy.contains('Next').click(); + cy.get(selector.nodes_0_host).type(data.host); + cy.get(selector.nodes_0_port).type(data.port); + cy.get(selector.nodes_0_weight).type(data.weight); + cy.contains('Next').click(); + + cy.contains('Next').click(); + cy.contains('Submit').click(); + cy.contains(data.submitSuccess); + + // back to route list page + cy.contains('Goto List').click(); + cy.url().should('contains', 'routes/list'); + }); + + it('Raw Data should have uri/host/remote_addr', () => { + cy.visit('/'); + cy.contains('Route').click(); + + cy.get(selector.nameSelector).type(data.name); + cy.contains('Search').click(); + cy.contains(data.name).siblings().contains('More').click(); + cy.contains('View').click(); + cy.get(selector.drawer).should('be.visible'); + + cy.get(selector.monacoScroll).within(() => { + cy.contains('uri').should('exist'); + cy.contains('host').should('exist'); + cy.contains('remote_addr').should('exist'); + }); + }); + + it('should create route with uris/hosts/remote_addrs', () => { + cy.visit('/'); + cy.contains('Route').click(); + + cy.get(selector.nameSelector).type(data.name); + cy.contains('Search').click(); + cy.contains(data.name).siblings().contains('Configure').click(); + + cy.get('#status').should('have.class', 'ant-switch-checked'); + + cy.get(selector.hosts_0).should('have.value', data.host_0); + cy.get(selector.uris_0).should('have.value', data.uri_0); + cy.get(selector.remote_addrs_0).should('have.value', data.remote_addr_0); + + cy.get(selector.addHost).click(); + cy.get(selector.hosts_1).type(data.host_1); + cy.get(selector.addUri).click(); + cy.get(selector.uris_1).clear().type(data.uri_1); + cy.get(selector.addRemoteAddr).click(); + cy.get(selector.remote_addrs_1).type(data.remote_addr_1); + + cy.contains('Next').click(); + cy.get(selector.nodes_0_host).type(data.host); + cy.get(selector.nodes_0_port).type(data.port); + cy.get(selector.nodes_0_weight).type(data.weight); + cy.contains('Next').click(); + + cy.contains('Next').click(); + cy.contains('Submit').click(); + cy.contains(data.submitSuccess); + + // back to route list page + cy.contains('Goto List').click(); + cy.url().should('contains', 'routes/list'); + }); + + it('Raw Data should have uris/hosts/remote_addrs', () => { + cy.visit('/'); + cy.contains('Route').click(); + + cy.get(selector.nameSelector).type(data.name); + cy.contains('Search').click(); + cy.contains(data.name).siblings().contains('More').click(); + cy.contains('View').click(); + cy.get(selector.drawer).should('be.visible'); + + cy.get(selector.monacoScroll).within(() => { + cy.contains('uris').should('exist'); + cy.contains('hosts').should('exist'); + cy.contains('remote_addrs').should('exist'); + }); + }); + + it('confirm the configure view render normally', () => { + cy.visit('/'); + cy.contains('Route').click(); + + cy.get(selector.nameSelector).type(data.name); + cy.contains('Search').click(); + cy.contains(data.name).siblings().contains('Configure').click(); + + cy.get('#status').should('have.class', 'ant-switch-checked'); + cy.get(selector.hosts_0).should('have.value', data.host_0); + cy.get(selector.hosts_1).should('have.value', data.host_1); + cy.get(selector.uris_0).should('have.value', data.uri_0); + cy.get(selector.uris_1).should('have.value', data.uri_1); + cy.get(selector.remote_addrs_0).should('have.value', data.remote_addr_0); + cy.get(selector.remote_addrs_1).should('have.value', data.remote_addr_1); + }); + + it('should delete the route', function () { + cy.visit('/routes/list'); + cy.get(selector.name).clear().type(data.name); + cy.contains('Search').click(); + cy.contains(data.name).siblings().contains('More').click(); + cy.contains('Delete').click(); + cy.get(selector.deleteAlert) + .should('be.visible') + .within(() => { + cy.contains('OK').click(); + }); + cy.get(selector.notification).should('contain', data.deleteRouteSuccess); + cy.get(selector.notificationCloseIcon).click(); + }); +}); diff --git a/web/src/components/Plugin/UI/limit-conn.tsx b/web/src/components/Plugin/UI/limit-conn.tsx index 6e358df0c9..a680b7d8b8 100644 --- a/web/src/components/Plugin/UI/limit-conn.tsx +++ b/web/src/components/Plugin/UI/limit-conn.tsx @@ -36,7 +36,7 @@ const FORM_ITEM_LAYOUT = { const LimitConn: React.FC = ({ form, schema }) => { const { formatMessage } = useIntl(); - const propertires = schema?.properties + const propertires = schema?.properties; return (
= ({ form, schema }) => { > @@ -85,7 +89,11 @@ const LimitConn: React.FC = ({ form, schema }) => { initialValue={propertires.rejected_code.default} tooltip={formatMessage({ id: 'component.pluginForm.limit-conn.rejected_code.tooltip' })} > - + ); diff --git a/web/src/components/Plugin/UI/plugin.tsx b/web/src/components/Plugin/UI/plugin.tsx index 088b50d059..13b66656d4 100644 --- a/web/src/components/Plugin/UI/plugin.tsx +++ b/web/src/components/Plugin/UI/plugin.tsx @@ -72,7 +72,7 @@ export const PluginForm: React.FC = ({ name, schema, renderForm, form }) case 'proxy-mirror': return ; case 'limit-conn': - return + return ; case 'referer-restriction': return ; default: diff --git a/web/src/pages/Route/transform.ts b/web/src/pages/Route/transform.ts index 91a3bc6535..af01464fe9 100644 --- a/web/src/pages/Route/transform.ts +++ b/web/src/pages/Route/transform.ts @@ -180,6 +180,19 @@ export const transformStepData = ({ unset(data.plugins, ['proxy-rewrite']); } + if (data.uris && data.uris.filter(Boolean).length === 1) { + [data.uri] = data.uris; + delete data.uris; + } + if (data.hosts && data.hosts.filter(Boolean).length === 1) { + [data.host] = data.hosts; + delete data.hosts; + } + if (data.remote_addrs && data.remote_addrs.filter(Boolean).length === 1) { + [data.remote_addr] = data.remote_addrs; + delete data.remote_addrs; + } + if ((Object.keys(redirect).length === 0 || redirect.http_to_https) && form2Data) { /** * Due to convertToRequestData under the Upstream component, @@ -216,7 +229,7 @@ export const transformStepData = ({ service_id.length === 0 ? 'service_id' : '', !Object.keys(data.plugins || {}).length ? 'plugins' : '', !Object.keys(data.script || {}).length ? 'script' : '', - form1Data.hosts.filter(Boolean).length === 0 ? 'hosts' : '', + form1Data.hosts?.filter(Boolean).length === 0 ? 'hosts' : '', form1Data.redirectOption === 'disabled' ? 'redirect' : '', data.remote_addrs?.filter(Boolean).length === 0 ? 'remote_addrs' : '', step3DataCloned.plugin_config_id === '' ? 'plugin_config_id' : '', @@ -234,16 +247,18 @@ export const transformStepData = ({ return pick(data, [ 'name', 'desc', - 'uris', 'methods', 'redirect', 'plugins', 'labels', 'enable_websocket', + data.uri ? 'uri' : 'uris', data.vars?.length ? 'vars' : '', service_id.length !== 0 ? 'service_id' : '', - form1Data.hosts.filter(Boolean).length !== 0 ? 'hosts' : '', + data.hosts?.filter(Boolean).length !== 0 ? 'hosts' : '', data.remote_addrs?.filter(Boolean).length !== 0 ? 'remote_addrs' : '', + data.host ? 'host' : '', + data.remote_addr ? 'remote_addr' : '', ]); }; @@ -287,6 +302,7 @@ export const transformRouteData = (data: RouteModule.Body) => { hosts, host, remote_addrs, + remote_addr, vars = [], status, upstream, @@ -302,7 +318,7 @@ export const transformRouteData = (data: RouteModule.Body) => { status, hosts: hosts || (host && [host]) || [''], uris: uris || (uri && [uri]) || [], - remote_addrs: remote_addrs || [''], + remote_addrs: remote_addrs || (remote_addr && [remote_addr]) || [''], // NOTE: API_VERSION is a system label custom_version_label: labels.API_VERSION || '', custom_normal_labels: Object.keys(labels) diff --git a/web/src/pages/Route/typing.d.ts b/web/src/pages/Route/typing.d.ts index 7ff75c575d..77bce06e58 100644 --- a/web/src/pages/Route/typing.d.ts +++ b/web/src/pages/Route/typing.d.ts @@ -71,8 +71,9 @@ declare namespace RouteModule { uri?: string; uris: string[]; host?: string; - hosts: string[]; - remote_addrs: string[]; + hosts?: string[]; + remote_addr?: string; + remote_addrs?: string[]; upstream: UpstreamComponent.ResponseData; vars: [string, Operator, string | any[]][]; upstream_path?: {