This is an automated email from the ASF dual-hosted git repository.
bzp2010 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 0273865d feat: add batch delete function for route (#2502)
0273865d is described below
commit 0273865d54eb60a5812c403a887398fe74d30a65
Author: 房森 <[email protected]>
AuthorDate: Tue Aug 9 10:23:16 2022 +0800
feat: add batch delete function for route (#2502)
Co-authored-by: Young <[email protected]>
Co-authored-by: Qi Guo <[email protected]>
Co-authored-by: Feng Han <[email protected]>
---
.../integration/route/batch-delete-route.spec.js | 165 +++++++++++++++++++++
web/src/locales/en-US/component.ts | 1 +
web/src/locales/tr-TR/component.ts | 1 +
web/src/locales/zh-CN/component.ts | 1 +
web/src/pages/Route/List.tsx | 32 +++-
web/src/pages/Route/locales/en-US.ts | 4 +
web/src/pages/Route/locales/tr-TR.ts | 4 +
web/src/pages/Route/locales/zh-CN.ts | 4 +
web/src/pages/Route/service.ts | 2 +-
9 files changed, 210 insertions(+), 4 deletions(-)
diff --git a/web/cypress/integration/route/batch-delete-route.spec.js
b/web/cypress/integration/route/batch-delete-route.spec.js
new file mode 100644
index 00000000..3a5c603a
--- /dev/null
+++ b/web/cypress/integration/route/batch-delete-route.spec.js
@@ -0,0 +1,165 @@
+/*
+ * 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 */
+
+context('Create and Batch Deletion Routes', () => {
+ const timeout = 5000;
+
+ const selector = {
+ name: '#name',
+ description: '#desc',
+ hosts_0: '#hosts_0',
+ uris_0: '#uris_0',
+ labels_0_labelKey: '#labels_0_labelKey',
+ labels_0_labelValue: '#labels_0_labelValue',
+ nodes_0_host: '#submitNodes_0_host',
+ nodes_0_port: '#submitNodes_0_port',
+ nodes_0_weight: '#submitNodes_0_weight',
+ nameSearchInput: '#name',
+ pathSearchInput: '#uri',
+ drawerBody: '.ant-drawer-wrapper-body',
+ notification: '.ant-notification-notice-message',
+ notificationClose: '.anticon-close',
+ };
+
+ const data = {
+ host1: '11.11.11.11',
+ host2: '12.12.12.12',
+ port: '80',
+ weight: 1,
+ uris: '/get',
+ uris0: '/get0',
+ uris1: '/get1',
+ uris2: '/get2',
+ urisx: '/getx',
+ submitSuccess: 'Submit Successfully',
+ deleteRouteSuccess: 'Delete Route Successfully',
+ test: 'test',
+ test0: 'test0',
+ test1: 'test1',
+ test2: 'test2',
+ testx: 'testx',
+ desc0: 'desc0',
+ desc1: 'desc1',
+ desc2: 'desc2',
+ value0: 'value0',
+ label0_value0: 'label0:value0',
+ };
+
+ beforeEach(() => {
+ cy.login();
+ });
+
+ it('should successfully create 3 routes', function () {
+ cy.visit('/');
+ cy.contains('Route').click();
+ for (let i = 0; i < 3; i += 1) {
+ cy.contains('Create').click();
+ cy.contains('Next').click().click();
+ cy.get(selector.name).type(`test${i}`);
+ cy.get(selector.description).type(`desc${i}`);
+ cy.get(selector.hosts_0).type(data.host1);
+ cy.get(selector.uris_0).clear().type(`/get${i}`);
+
+ // config label
+ cy.contains('Manage').click();
+
+ // eslint-disable-next-line no-loop-func
+ cy.get(selector.drawerBody).within(() => {
+ cy.contains('button', 'Add')
+ .should('not.be.disabled')
+ .click()
+ .then(() => {
+ cy.get(selector.labels_0_labelKey).type(`label${i}`);
+ cy.get(selector.labels_0_labelValue).type(`value${i}`);
+ cy.contains('Confirm').click();
+ });
+ });
+
+ cy.contains('button', 'Next').should('not.be.disabled').click();
+ cy.get(selector.nodes_0_host).type(data.host2, {
+ timeout,
+ });
+ 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);
+ cy.contains('Goto List').click();
+ cy.url().should('contains', 'routes/list');
+ }
+ });
+
+ it('should delete the route', function () {
+ cy.visit('/routes/list');
+ cy.contains(data.test0).get('[type="checkbox"]').check();
+ cy.contains(data.test2).get('[type="checkbox"]').check();
+ cy.contains('BatchDeletion Routes').should('be.visible').click({ timeout
});
+ cy.get(selector.notification).should('contain', data.deleteRouteSuccess);
+ cy.get(selector.notificationClose).should('be.visible').click({
+ force: true,
+ multiple: true,
+ });
+ });
+
+ it('should batch delete the name of the route', function () {
+ cy.visit('/');
+ cy.contains('Route').click();
+ // full match
+ cy.get(selector.nameSearchInput).type(data.test0);
+ cy.contains('Search').click();
+ cy.contains(data.test1).should('not.exist');
+ cy.contains(data.test0).should('not.exist');
+ cy.contains(data.test2).should('not.exist');
+ // partial match
+ cy.get(selector.nameSearchInput).clear().type(data.test2);
+ cy.contains('Search').click();
+ cy.contains(data.test0).should('not.exist');
+ cy.contains(data.test1).should('not.exist');
+ cy.contains(data.test2).should('not.exist');
+ // no match
+ cy.get(selector.nameSearchInput).clear().type(data.testx);
+ cy.contains('Search').click();
+ cy.contains(data.test0).should('not.exist');
+ cy.contains(data.test1).should('not.exist');
+ cy.contains(data.test2).should('not.exist');
+ });
+
+ it('should batch delete the path of the route', function () {
+ cy.visit('/');
+ cy.contains('Route').click();
+ // full match
+ cy.get(selector.pathSearchInput).type(data.uris0);
+ cy.contains('Search').click();
+ cy.contains(data.uris1).should('not.exist');
+ cy.contains(data.uris0).should('not.exist');
+ cy.contains(data.uris2).should('not.exist');
+ // partial match
+ cy.get(selector.pathSearchInput).clear().type(data.uris2);
+ cy.contains('Search').click();
+ cy.contains(data.uris0).should('not.exist');
+ cy.contains(data.uris1).should('not.exist');
+ cy.contains(data.uris2).should('not.exist');
+ // no match
+ cy.get(selector.pathSearchInput).clear().type(data.urisx);
+ cy.contains('Search').click();
+ cy.contains(data.uris0).should('not.exist');
+ cy.contains(data.uris1).should('not.exist');
+ cy.contains(data.uris2).should('not.exist');
+ });
+});
diff --git a/web/src/locales/en-US/component.ts
b/web/src/locales/en-US/component.ts
index 10db5d53..fb117214 100644
--- a/web/src/locales/en-US/component.ts
+++ b/web/src/locales/en-US/component.ts
@@ -53,6 +53,7 @@ export default {
'component.global.notification.success.message.deleteSuccess': 'Deleted
Successfully',
'component.global.create.consumer.success': 'Create Consumer Successfully',
'component.global.delete.consumer.success': 'Delete Consumer Successfully',
+ 'component.global.delete.routes.success':'Delete Route Successfully',
'component.global.edit.consumer.success': 'Edit Consumer Successfully',
'component.global.steps.stepTitle.basicInformation': 'Basic Information',
diff --git a/web/src/locales/tr-TR/component.ts
b/web/src/locales/tr-TR/component.ts
index f4df0435..031355cb 100644
--- a/web/src/locales/tr-TR/component.ts
+++ b/web/src/locales/tr-TR/component.ts
@@ -53,6 +53,7 @@ export default {
'component.global.notification.success.message.deleteSuccess': 'Başarıyla
silindi',
'component.global.create.consumer.success': 'Başarıyla oluşturuldu',
'component.global.delete.consumer.success': 'Başarıyla silindi',
+ 'component.global.delete.routes.success':'Sil Yöneltmeler Başarılı',
'component.global.edit.consumer.success': 'Başarıyla güncellendi',
'component.global.steps.stepTitle.basicInformation': 'Temel Bilgiler',
diff --git a/web/src/locales/zh-CN/component.ts
b/web/src/locales/zh-CN/component.ts
index 6131a42b..a7902e0c 100644
--- a/web/src/locales/zh-CN/component.ts
+++ b/web/src/locales/zh-CN/component.ts
@@ -60,6 +60,7 @@ export default {
'component.global.updateTime': '更新时间',
'component.global.create.consumer.success': '创建消费者成功',
'component.global.delete.consumer.success': '删除消费者成功',
+ 'component.global.delete.routes.success':'删除路由成功',
'component.global.edit.consumer.success': '配置消费者成功',
'component.global.popconfirm.title.delete': '确定删除该条记录吗?',
diff --git a/web/src/pages/Route/List.tsx b/web/src/pages/Route/List.tsx
index f4f36bd5..a897108d 100755
--- a/web/src/pages/Route/List.tsx
+++ b/web/src/pages/Route/List.tsx
@@ -31,6 +31,7 @@ import {
Modal,
Menu,
Dropdown,
+ Table,
Tooltip,
} from 'antd';
import { history, useIntl } from 'umi';
@@ -96,6 +97,8 @@ const Page: React.FC = () => {
setSelectedRowKeys(currentSelectKeys);
},
preserveSelectedRowKeys: true,
+ selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT,
Table.SELECTION_NONE],
+ defaultSelectedRowKeys: [1],
};
const handleTableActionSuccessResponse = (msgTip: string) => {
@@ -306,7 +309,7 @@ const Page: React.FC = () => {
{
title: formatMessage({ id: 'component.global.name' }),
dataIndex: 'name',
- fixed: 'left',
+ width: 150,
},
{
title: formatMessage({ id: 'component.global.id' }),
@@ -542,6 +545,31 @@ const Page: React.FC = () => {
actionRef={ref}
rowKey="id"
columns={columns}
+ rowSelection={rowSelection}
+ tableAlertRender={() => (
+ <Space size={24}>
+ <span>
+ {formatMessage({ id: 'page.route.chosen' })}
{selectedRowKeys.length} {formatMessage({ id: 'page.route.item' })}
+ </span>
+ </Space>
+ )}
+ tableAlertOptionRender={() => {
+ return (
+ <Space size={16}>
+ <Button
+ onClick={async () => {
+ await remove(selectedRowKeys).then(() => {
+ handleTableActionSuccessResponse(
+ `${formatMessage({ id:
'component.global.delete.routes.success' })}`,
+ );
+ });
+ ref.current?.reloadAndRest?.();
+ }}>
+ {formatMessage({ id: 'page.route.batchDeletion' })}
+ </Button>
+ </Space>
+ );
+ }}
request={fetchList}
pagination={{
onChange: (page, pageSize?) => savePageList(page, pageSize),
@@ -559,9 +587,7 @@ const Page: React.FC = () => {
</Button>,
<ListToolbar />,
]}
- rowSelection={rowSelection}
footer={() => <ListFooter />}
- tableAlertRender={false}
scroll={{ x: 1300 }}
/>
<DebugDrawView
diff --git a/web/src/pages/Route/locales/en-US.ts
b/web/src/pages/Route/locales/en-US.ts
index 5fcfc284..3ba20111 100644
--- a/web/src/pages/Route/locales/en-US.ts
+++ b/web/src/pages/Route/locales/en-US.ts
@@ -43,6 +43,10 @@ export default {
'page.route.import': 'Import',
'page.route.createRoute': 'Create Route',
'page.route.editRoute': 'Configure Route',
+ 'page.route.batchDeletion': 'BatchDeletion Routes',
+ 'page.route.unSelect': 'Unselect',
+ 'page.route.item': 'items',
+ 'page.route.chosen':'chosen',
'page.route.input.placeholder.parameterNameHttpHeader': 'Request header
name, for example: HOST',
'page.route.input.placeholder.parameterNameRequestParameter': 'Parameter
name, for example: id',
diff --git a/web/src/pages/Route/locales/tr-TR.ts
b/web/src/pages/Route/locales/tr-TR.ts
index d14f3d00..6bd6e6a2 100644
--- a/web/src/pages/Route/locales/tr-TR.ts
+++ b/web/src/pages/Route/locales/tr-TR.ts
@@ -43,6 +43,10 @@ export default {
'page.route.import': 'İçe aktar',
'page.route.createRoute': 'Yeni yönlendirme oluştur',
'page.route.editRoute': 'Yönlendirme düzenle',
+ 'page.route.batchDeletion': 'Toplu yolu sil',
+ 'page.route.unSelect': 'Seçilme',
+ 'page.route.item': 'term',
+ 'page.route.chosen':'Seçili',
'page.route.input.placeholder.parameterNameHttpHeader': 'Request header adı
örn :HOST',
'page.route.input.placeholder.parameterNameRequestParameter': 'Parameter adı
örn :userId',
diff --git a/web/src/pages/Route/locales/zh-CN.ts
b/web/src/pages/Route/locales/zh-CN.ts
index dce4a763..3b5bc470 100644
--- a/web/src/pages/Route/locales/zh-CN.ts
+++ b/web/src/pages/Route/locales/zh-CN.ts
@@ -51,6 +51,10 @@ export default {
'page.route.import': '导入',
'page.route.createRoute': '创建路由',
'page.route.editRoute': '编辑路由',
+ 'page.route.batchDeletion': '批量删除路由',
+ 'page.route.unSelect': '取消选择',
+ 'page.route.item': '项',
+ 'page.route.chosen':'已选择',
// button
'page.route.button.returnList': '返回路由列表',
diff --git a/web/src/pages/Route/service.ts b/web/src/pages/Route/service.ts
index ae317c5d..bba3681c 100644
--- a/web/src/pages/Route/service.ts
+++ b/web/src/pages/Route/service.ts
@@ -59,7 +59,7 @@ export const fetchList = ({ current = 1, pageSize = 10,
...res }) => {
});
};
-export const remove = (rid: string) => request(`/routes/${rid}`, { method:
'DELETE' });
+export const remove = (rid: string[]) => request(`/routes/${rid}`, { method:
'DELETE' });
export const checkUniqueName = (name = '', exclude = '') =>
request('/notexist/routes', {