You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by li...@apache.org on 2021/07/30 00:22:30 UTC

[apisix-dashboard] branch master updated: fix: make service chash key Input inputable and selectable (#1982)

This is an automated email from the ASF dual-hosted git repository.

liuxiran pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git


The following commit(s) were added to refs/heads/master by this push:
     new d49b283  fix: make service chash key Input inputable and selectable (#1982)
d49b283 is described below

commit d49b28381bc68c4da1552fe953c1ea7080423f4b
Author: foolwc <29...@qq.com>
AuthorDate: Fri Jul 30 08:22:22 2021 +0800

    fix: make service chash key Input inputable and selectable (#1982)
    
    
    
    Co-authored-by: li wencheng <we...@daocloud.io>
    Co-authored-by: litesun <su...@apache.org>
---
 .../route/create-route-with-chash-upstream.spec.js | 124 +++++++++++++++++++++
 .../create-service-with-chash-upstream.spec.js     | 106 ++++++++++++++++++
 ...and_edit_upstream_with_custom_chash_key.spec.js | 109 ++++++++++++++++++
 web/src/components/Upstream/components/Type.tsx    |  29 +++--
 4 files changed, 358 insertions(+), 10 deletions(-)

diff --git a/web/cypress/integration/route/create-route-with-chash-upstream.spec.js b/web/cypress/integration/route/create-route-with-chash-upstream.spec.js
new file mode 100644
index 0000000..f2aef94
--- /dev/null
+++ b/web/cypress/integration/route/create-route-with-chash-upstream.spec.js
@@ -0,0 +1,124 @@
+/*
+ * 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 Edit Route With Custom CHash Key Upstream', () => {
+  const selector = {
+    name: '#name',
+    menu: '[role=menu]',
+    roundRobinSelect: '[title="Round Robin"]',
+    varSelect: '[title="vars"]',
+    defaultCHashKey: '[value="remote_addr"]',
+    upstreamType: ".ant-select-item-option-content",
+    hashPosition: ".ant-select-item-option-content",
+    nodes_0_host: '#nodes_0_host',
+    nodes_0_port: '#nodes_0_port',
+    nodes_0_weight: '#nodes_0_weight',
+    nameSelector: '[title=Name]',
+    chash_key: '#key',
+    deleteAlert: '.ant-modal-body',
+    notificationCloseIcon: '.ant-notification-close-icon',
+    notification: '.ant-notification-notice-message',
+  };
+
+  const data = {
+    routeName: 'roteName',
+    ip: '127.0.0.1',
+    port: '7000',
+    weight: '1',
+    deleteRouteSuccess: 'Delete Route Successfully',
+    submitSuccess: 'Submit Successfully',
+    custom_key: 'custom_key',
+    new_key: 'new_key',
+  };
+
+  beforeEach(() => {
+    cy.login();
+  });
+
+  it('should create route with custom chash key Upstream', function () {
+    cy.visit('/');
+    cy.get(selector.menu)
+      .should('be.visible')
+      .within(() => {
+        cy.contains('Route').click();
+      });
+    cy.contains('Create').click();
+
+    cy.contains('Next').click().click();
+    cy.get(selector.name).type(data.routeName);
+    cy.contains('Next').click();
+
+    cy.get(selector.roundRobinSelect).click();
+    cy.get(selector.upstreamType).within(() => {
+      cy.contains('CHash').click();
+    });
+    cy.get(selector.varSelect).click();
+    cy.get(selector.hashPosition).within(() => {
+      cy.contains('cookie').click();
+    });
+    cy.get(selector.defaultCHashKey).click();
+    cy.get(selector.defaultCHashKey).clear().type(data.custom_key);
+    cy.get(selector.nodes_0_host).click();
+    cy.get(selector.nodes_0_host).type(data.ip);
+    cy.get(selector.nodes_0_port).clear().type(data.port);
+    cy.get(selector.nodes_0_weight).clear().type(data.weight);
+
+    cy.contains('Next').click();
+    cy.contains('Next').click();
+    cy.contains('Submit').click();
+    cy.contains(data.submitSuccess).should('be.visible');
+
+    // back to route list page
+    cy.contains('Goto List').click();
+    cy.url().should('contains', 'routes/list');
+  });
+
+  it('should edit this route ', function () {
+    cy.visit('/');
+    cy.contains('Route').click();
+    cy.get(selector.nameSelector).type(data.routeName);
+
+    cy.contains('Search').click();
+    cy.contains(data.routeName).siblings().contains('Configure').click();
+    cy.get(selector.name).should('value', data.routeName);
+    cy.contains('Next').click({
+      force: true,
+    });
+    cy.get(selector.chash_key).should('value', data.custom_key);
+    cy.get(selector.chash_key).clear().type(data.new_key);
+    cy.contains('Next').click();
+    cy.contains('Next').click();
+    cy.contains('Submit').click();
+    cy.contains(data.submitSuccess).should('be.visible');
+  });
+
+  it('should delete the route', function () {
+    cy.visit('/routes/list');
+    cy.get(selector.name).clear().type(data.routeName);
+    cy.contains('Search').click();
+    cy.contains(data.routeName).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({multiple: true});
+  });
+});
diff --git a/web/cypress/integration/service/create-service-with-chash-upstream.spec.js b/web/cypress/integration/service/create-service-with-chash-upstream.spec.js
new file mode 100644
index 0000000..e613586
--- /dev/null
+++ b/web/cypress/integration/service/create-service-with-chash-upstream.spec.js
@@ -0,0 +1,106 @@
+/*
+ * 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 Edit Service with Custom CHash Key Upstream', () => {
+  const selector = {
+    name: '#name',
+    description: '#desc',
+    roundRobinSelect: '[title="Round Robin"]',
+    varSelect: '[title="vars"]',
+    defaultCHashKey: '[value="remote_addr"]',
+    nodes_0_host: '#nodes_0_host',
+    nodes_0_port: '#nodes_0_port',
+    nodes_0_weight: '#nodes_0_weight',
+    upstreamType: ".ant-select-item-option-content",
+    hashPosition: ".ant-select-item-option-content",
+    chash_key: '#key',
+    notification: '.ant-notification-notice-message',
+    nameSearch: '[title=Name]',
+    notificationCloseIcon: '.ant-notification-close-icon',
+  };
+
+  const data = {
+    serviceName: 'chash-service',
+    createServiceSuccess: 'Create Service Successfully',
+    deleteServiceSuccess: 'Delete Service Successfully',
+    editServiceSuccess: 'Configure Service Successfully',
+    port: '80',
+    weight: 1,
+    description: 'desc_by_autotest',
+    ip1: '127.0.0.1',
+    port0: '7000',
+    weight0: '1',
+    custom_key: 'custom_key',
+    new_key: 'new_key',
+  };
+
+  beforeEach(() => {
+    cy.login();
+  });
+
+  it('should create a service with custom CHash key Upstream', function () {
+    cy.visit('/');
+    cy.contains('Service').click();
+    cy.contains('Create').click();
+    cy.get(selector.name).type(data.serviceName);
+    cy.get(selector.description).type(data.description);
+    cy.get(selector.roundRobinSelect).click();
+    cy.get(selector.upstreamType).within(() => {
+      cy.contains('CHash').click();
+    });
+    cy.get(selector.varSelect).click();
+    cy.get(selector.hashPosition).within(() => {
+      cy.contains('cookie').click();
+    });
+    cy.get(selector.defaultCHashKey).click();
+    cy.get(selector.defaultCHashKey).clear().type('custom_key');
+    cy.get(selector.nodes_0_host).click();
+    cy.get(selector.nodes_0_host).type(data.ip1);
+    cy.get(selector.nodes_0_port).clear().type(data.port0);
+    cy.get(selector.nodes_0_weight).clear().type(data.weight0);
+    cy.contains('Next').click();
+    cy.contains('Next').click();
+    cy.contains('Submit').click();
+    cy.get(selector.notification).should('contain', data.createServiceSuccess);
+  });
+
+  it('should edit the service', function () {
+    cy.visit('/service/list');
+
+    cy.get(selector.nameSearch).type(data.serviceName);
+    cy.contains('Search').click();
+    cy.contains(data.serviceName).siblings().contains('Configure').click();
+    cy.get(selector.chash_key).should('value', data.custom_key);
+    cy.get(selector.chash_key).clear().type(data.new_key);
+    cy.contains('Next').click();
+    cy.contains('Next').click();
+    cy.contains('Submit').click();
+    cy.get(selector.notification).should('contain', data.editServiceSuccess);
+  });
+
+
+  it('should delete this service', function () {
+    cy.visit('/service/list');
+    cy.get(selector.nameSearch).type(data.serviceName);
+    cy.contains('Search').click();
+    cy.contains(data.serviceName).siblings().contains('Delete').click();
+    cy.contains('button', 'Confirm').click();
+    cy.get(selector.notification).should('contain', data.deleteServiceSuccess);
+    cy.get(selector.notificationCloseIcon).click();
+  });
+});
diff --git a/web/cypress/integration/upstream/create_and_edit_upstream_with_custom_chash_key.spec.js b/web/cypress/integration/upstream/create_and_edit_upstream_with_custom_chash_key.spec.js
new file mode 100644
index 0000000..dda1145
--- /dev/null
+++ b/web/cypress/integration/upstream/create_and_edit_upstream_with_custom_chash_key.spec.js
@@ -0,0 +1,109 @@
+/*
+ * 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 Upstream With Custom CHash Key', () => {
+  const selector = {
+    name: '#name',
+    description: '#desc',
+    roundRobinSelect: '[title="Round Robin"]',
+    varSelect: '[title="vars"]',
+    defaultCHashKey: '[value="remote_addr"]',
+    nodes_0_host: '#nodes_0_host',
+    nodes_0_port: '#nodes_0_port',
+    nodes_0_weight: '#nodes_0_weight',
+    upstreamType: ".ant-select-item-option-content",
+    hashPosition: ".ant-select-item-option-content",
+    chash_key: '#key',
+    notification: '.ant-notification-notice-message',
+    nameSelector: '[title=Name]',
+  };
+
+  const data = {
+    upstreamName: 'test_upstream',
+    description: 'desc_by_autotest',
+    custom_key: 'custom_key',
+    new_key: 'new_key',
+    ip: '127.0.0.1',
+    port: '7000',
+    weight: '1',
+    createUpstreamSuccess: 'Create Upstream Successfully',
+    configureUpstreamSuccess: 'Configure Upstream Successfully',
+    deleteUpstreamSuccess: 'Delete Upstream Successfully',
+  };
+
+  beforeEach(() => {
+    cy.login();
+  });
+
+  it('should create upstream with custom chash key', function () {
+    cy.visit('/');
+    cy.contains('Upstream').click();
+    cy.contains('Create').click();
+
+    cy.get(selector.name).type(data.upstreamName);
+    cy.get(selector.description).type(data.description);
+
+    cy.get(selector.roundRobinSelect).click();
+    cy.get(selector.upstreamType).within(() => {
+      cy.contains('CHash').click();
+    });
+    cy.get(selector.varSelect).click();
+    cy.get(selector.hashPosition).within(() => {
+      cy.contains('cookie').click();
+    });
+    cy.get(selector.defaultCHashKey).click();
+    cy.get(selector.defaultCHashKey).clear().type(data.custom_key);
+    cy.get(selector.nodes_0_host).click();
+    cy.get(selector.nodes_0_host).type(data.ip);
+    cy.get(selector.nodes_0_port).clear().type(data.port);
+    cy.get(selector.nodes_0_weight).clear().type(data.weight);
+
+    cy.contains('Next').click();
+    cy.contains('Submit').click();
+    cy.get(selector.notification).should('contain', data.createUpstreamSuccess);
+    cy.url().should('contains', 'upstream/list');
+  });
+
+  it('should configure the upstream', function () {
+    cy.visit('/');
+    cy.contains('Upstream').click();
+
+    cy.get(selector.nameSelector).type(data.upstreamName);
+    cy.contains('Search').click();
+    cy.contains(data.upstreamName).siblings().contains('Configure').click();
+
+    cy.get(selector.chash_key).should('value', data.custom_key);
+    cy.get(selector.chash_key).clear().type(data.new_key);
+
+    cy.contains('Next').click();
+    cy.contains('Submit').click({
+      force: true,
+    });
+
+    cy.get(selector.notification).should('contain', data.configureUpstreamSuccess);
+    cy.url().should('contains', 'upstream/list');
+  });
+
+  it('should delete the upstream', function () {
+    cy.visit('/');
+    cy.contains('Upstream').click();
+    cy.contains(data.upstreamName).siblings().contains('Delete').click();
+    cy.contains('button', 'Confirm').click();
+    cy.get(selector.notification).should('contain', data.deleteUpstreamSuccess);
+  });
+});
diff --git a/web/src/components/Upstream/components/Type.tsx b/web/src/components/Upstream/components/Type.tsx
index 8788ebb..ed65670 100644
--- a/web/src/components/Upstream/components/Type.tsx
+++ b/web/src/components/Upstream/components/Type.tsx
@@ -14,8 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react'
-import { Form, Select } from 'antd'
+import React, { useState } from 'react'
+import { AutoComplete, Form, Select } from 'antd'
 import { useIntl } from 'umi'
 import type { FormInstance } from 'antd/es/form'
 
@@ -28,6 +28,11 @@ type Props = {
 
 const CHash: React.FC<Pick<Props, 'readonly'>> = ({ readonly }) => {
   const { formatMessage } = useIntl()
+  const [keySearchWord, setKeySearchWord] = useState('');
+
+  const handleSearch = (search: string) => {
+    setKeySearchWord(search);
+  };
   return (
     <React.Fragment>
       <Form.Item name="hash_on" rules={[{ required: true }]} label={formatMessage({ id: 'component.upstream.fields.hash_on' })} tooltip={formatMessage({ id: 'component.upstream.fields.hash_on.tooltip' })} initialValue="vars">
@@ -39,14 +44,18 @@ const CHash: React.FC<Pick<Props, 'readonly'>> = ({ readonly }) => {
           ))}
         </Select>
       </Form.Item>
-      <Form.Item name="key" rules={[{ required: true }]} label={formatMessage({ id: 'component.upstream.fields.key' })} tooltip={formatMessage({ id: 'component.upstream.fields.key.tooltip' })} initialValue="remote_addr">
-        <Select disabled={readonly}>
-          {Object.entries(CommonHashKeyEnum).map(([label, value]) => (
-            <Select.Option value={value} key={value}>
-              {label}
-            </Select.Option>
-          ))}
-        </Select>
+      <Form.Item name="key" rules={[{ required: true }]}
+                 label={formatMessage({ id: 'component.upstream.fields.key' })}
+                 tooltip={formatMessage({ id: 'component.upstream.fields.key.tooltip' })} initialValue="remote_addr">
+        <AutoComplete disabled={readonly} onSearch={handleSearch}>
+          {Object.entries(CommonHashKeyEnum)
+            .filter((([label, value]) => label.startsWith(keySearchWord) || value.startsWith(keySearchWord)))
+            .map(([label, value]) => (
+              <Select.Option value={value} key={value}>
+                {label}
+              </Select.Option>
+            ))}
+        </AutoComplete>
       </Form.Item>
     </React.Fragment>
   )