diff --git a/web/cypress/integration/consumer/create-consumer-with-proxy-mirror-form.spec.js b/web/cypress/integration/consumer/create-consumer-with-proxy-mirror-form.spec.js new file mode 100644 index 0000000000..5e21b80764 --- /dev/null +++ b/web/cypress/integration/consumer/create-consumer-with-proxy-mirror-form.spec.js @@ -0,0 +1,103 @@ +/* + * 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 and Delete Consumer', () => { + beforeEach(() => { + cy.login(); + + cy.fixture('selector.json').as('domSelector'); + cy.fixture('data.json').as('data'); + }); + + const selector = { + host: "#host" + } + + const data = { + correctHost: 'http://127.0.0.1', + wrongHost: '127.0.0.1:1999' + } + + it('creates consumer with proxy-mirror form', function () { + cy.visit('/'); + cy.contains('Consumer').click(); + cy.get(this.domSelector.empty).should('be.visible'); + cy.contains('Create').click(); + // basic information + cy.get(this.domSelector.username).type(this.data.consumerName); + cy.get(this.domSelector.description).type(this.data.description); + cy.contains('Next').click(); + + // config auth plugin + cy.contains(this.domSelector.pluginCard, 'key-auth').within(() => { + cy.contains('Enable').click({ + force: true, + }); + }); + cy.focused(this.domSelector.drawer).should('exist'); + cy.get(this.domSelector.disabledSwitcher).click(); + // edit codemirror + cy.get(this.domSelector.codeMirror) + .first() + .then((editor) => { + editor[0].CodeMirror.setValue( + JSON.stringify({ + key: 'test', + }), + ); + cy.contains('button', 'Submit').click(); + }); + + cy.contains(this.domSelector.pluginCard, 'proxy-mirror').within(() => { + cy.contains('Enable').click({ + force: true, + }); + }); + + cy.focused(this.domSelector.drawer).should('exist'); + + // config proxy-mirror form + cy.get(selector.host).type(data.wrongHost); + cy.get(this.domSelector.drawer).within(() => { + cy.contains('Submit').click({ + force: true, + }); + }); + cy.get(this.domSelector.notification).should('contain', 'Invalid plugin data'); + + cy.get(selector.host).clear().type(data.correctHost); + cy.get(this.domSelector.disabledSwitcher).click(); + cy.get(this.domSelector.drawer).within(() => { + cy.contains('Submit').click({ + force: true, + }); + }); + cy.get(this.domSelector.drawer).should('not.exist'); + + cy.contains('button', 'Next').click(); + cy.contains('button', 'Submit').click(); + cy.get(this.domSelector.notification).should('contain', this.data.createConsumerSuccess); + }); + + it('delete the consumer', function () { + cy.visit('/consumer/list'); + cy.contains(this.data.consumerName).should('be.visible').siblings().contains('Delete').click(); + cy.contains('button', 'Confirm').click(); + cy.get(this.domSelector.notification).should('contain', this.data.deleteConsumerSuccess); + }); +}); diff --git a/web/cypress/support/commands.js b/web/cypress/support/commands.js index a55a8d0648..d3723c6050 100644 --- a/web/cypress/support/commands.js +++ b/web/cypress/support/commands.js @@ -66,6 +66,18 @@ Cypress.Commands.add('configurePlugins', (cases) => { // NOTE: wait for the Drawer to appear on the DOM cy.focused(domSelector.drawer).should('exist'); + + cy.get(domSelector.codeMirrorMode).invoke('text').then(text => { + if (text === 'Form') { + // FIXME: https://github.com/cypress-io/cypress/issues/7306 + cy.wait(5000); + cy.get(domSelector.codeMirrorMode).should('be.visible'); + cy.get(domSelector.codeMirrorMode).click(); + cy.get(domSelector.selectDropdown).should('be.visible'); + cy.get(domSelector.selectJSON).click(); + } + }); + cy.get(domSelector.drawer, { timeout }).within(() => { cy.get(domSelector.switch).click({ force: true, diff --git a/web/src/components/Plugin/UI/plugin.tsx b/web/src/components/Plugin/UI/plugin.tsx index 79a2df5754..c045750121 100644 --- a/web/src/components/Plugin/UI/plugin.tsx +++ b/web/src/components/Plugin/UI/plugin.tsx @@ -20,6 +20,7 @@ import { Empty } from 'antd'; import { useIntl } from 'umi'; import BasicAuth from './basic-auth'; +import ProxyMirror from './proxy-mirror'; import LimitConn from './limit-conn'; type Props = { @@ -28,7 +29,7 @@ type Props = { renderForm: boolean } -export const PLUGIN_UI_LIST = ['basic-auth', 'limit-conn']; +export const PLUGIN_UI_LIST = ['basic-auth', 'limit-conn', 'proxy-mirror']; export const PluginForm: React.FC = ({ name, renderForm, form }) => { @@ -39,6 +40,8 @@ export const PluginForm: React.FC = ({ name, renderForm, form }) => { switch (name) { case 'basic-auth': return + case 'proxy-mirror': + return case 'limit-conn': return default: diff --git a/web/src/components/Plugin/UI/proxy-mirror.tsx b/web/src/components/Plugin/UI/proxy-mirror.tsx new file mode 100644 index 0000000000..6fe6f40315 --- /dev/null +++ b/web/src/components/Plugin/UI/proxy-mirror.tsx @@ -0,0 +1,62 @@ +/* + * 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 React from 'react'; +import type { FormInstance } from 'antd/es/form'; +import { Form, Input } from 'antd'; +import { useIntl } from 'umi'; + +type Props = { + form: FormInstance; +}; + +const FORM_ITEM_LAYOUT = { + labelCol: { + span: 4, + }, + wrapperCol: { + span: 10 + }, +}; + +const ProxyMirror: React.FC = ({ form }) => { + const { formatMessage } = useIntl(); + + return ( +
+ + + +
+ ); +} + +export default ProxyMirror; diff --git a/web/src/components/Plugin/locales/en-US.ts b/web/src/components/Plugin/locales/en-US.ts index 2b63c63573..b8069edc7e 100644 --- a/web/src/components/Plugin/locales/en-US.ts +++ b/web/src/components/Plugin/locales/en-US.ts @@ -22,6 +22,11 @@ export default { 'component.plugin.pluginTemplate.tip1': '1. When a route already have plugins field configured, the plugins in the plugin template will be merged into it.', 'component.plugin.pluginTemplate.tip2': '2. The same plugin in the plugin template will override one in the plugins', + // proxxy-mirror + 'component.pluginForm.proxy-mirror.host.tooltip': 'Specify a mirror service address, e.g. http://127.0.0.1:9797 (address needs to contain schema: http or https, not URI part)', + 'component.pluginForm.proxy-mirror.host.extra': 'e.g. http://127.0.0.1:9797 (address needs to contain schema: http or https, not URI part)', + 'component.pluginForm.proxy-mirror.host.ruletip': 'address needs to contain schema: http or https, not URI part', + // limit-conn 'component.pluginForm.limit-conn.conn.tooltip': 'the maximum number of concurrent requests allowed. Requests exceeding this ratio (and below conn + burst) will get delayed(the latency seconds is configured by default_conn_delay) to conform to this threshold.', 'component.pluginForm.limit-conn.burst.tooltip': 'the number of excessive concurrent requests (or connections) allowed to be delayed.', diff --git a/web/src/components/Plugin/locales/zh-CN.ts b/web/src/components/Plugin/locales/zh-CN.ts index c106b2e0dc..8642439670 100644 --- a/web/src/components/Plugin/locales/zh-CN.ts +++ b/web/src/components/Plugin/locales/zh-CN.ts @@ -22,6 +22,11 @@ export default { 'component.plugin.pluginTemplate.tip1': '1. 若路由已配置插件,则插件模板数据将与已配置的插件数据合并。', 'component.plugin.pluginTemplate.tip2': '2. 插件模板相同的插件会覆盖掉原有的插件。', + // proxxy-mirror + 'component.pluginForm.proxy-mirror.host.tooltip': '指定镜像服务地址,例如:http://127.0.0.1:9797(地址中需要包含 schema :http或https,不能包含 URI 部分)', + 'component.pluginForm.proxy-mirror.host.extra': '例如:http://127.0.0.1:9797(地址中需要包含 schema:http或https,不能包含 URI 部分)', + 'component.pluginForm.proxy-mirror.host.ruletip': '地址中需要包含 schema :http或https,不能包含 URI 部分', + // limit-conn 'component.pluginForm.limit-conn.conn.tooltip': '允许的最大并发请求数。超过 conn 的限制、但是低于 conn + burst 的请求,将被延迟处理。', 'component.pluginForm.limit-conn.burst.tooltip': '允许被延迟处理的并发请求数。',