You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by ma...@apache.org on 2021/04/25 05:51:29 UTC
[apisix-dashboard] branch master updated: fix: can not configure
upstream with no nodes (#1812)
This is an automated email from the ASF dual-hosted git repository.
majunjie 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 1dce703 fix: can not configure upstream with no nodes (#1812)
1dce703 is described below
commit 1dce703dba0c0f773cb4deb8545e0b671a39e8db
Author: liuxiran <li...@apache.org>
AuthorDate: Sun Apr 25 13:51:22 2021 +0800
fix: can not configure upstream with no nodes (#1812)
---
api/internal/core/entity/format.go | 2 +-
api/internal/core/entity/format_test.go | 76 ++++++++++++++++++++++
web/cypress/fixtures/data.json | 1 +
web/cypress/fixtures/selector.json | 1 +
.../create_and_edit_upstream_with_no_nodes.spec.js | 68 +++++++++++++++++++
web/src/components/Upstream/components/Nodes.tsx | 2 +-
web/src/components/Upstream/service.ts | 15 ++---
web/src/pages/Route/typing.d.ts | 1 -
web/src/pages/Upstream/locales/en-US.ts | 2 +-
web/src/pages/Upstream/typing.d.ts | 1 -
10 files changed, 154 insertions(+), 15 deletions(-)
diff --git a/api/internal/core/entity/format.go b/api/internal/core/entity/format.go
index d92bb40..1043e81 100644
--- a/api/internal/core/entity/format.go
+++ b/api/internal/core/entity/format.go
@@ -46,7 +46,7 @@ func mapKV2Node(key string, val float64) (*Node, error) {
}
func NodesFormat(obj interface{}) interface{} {
- var nodes []*Node
+ nodes := make([]*Node, 0)
switch objType := obj.(type) {
case map[string]float64:
log.Infof("nodes type: %v", objType)
diff --git a/api/internal/core/entity/format_test.go b/api/internal/core/entity/format_test.go
index 7ca9d4c..23ff9f1 100644
--- a/api/internal/core/entity/format_test.go
+++ b/api/internal/core/entity/format_test.go
@@ -101,3 +101,79 @@ func TestNodesFormat_Map(t *testing.T) {
assert.Contains(t, jsonStr, `"port":8080`)
assert.Contains(t, jsonStr, `"host":"127.0.0.1"`)
}
+
+func TestNodesFormat_empty_struct(t *testing.T) {
+ // route data saved in ETCD
+ routeStr := `{
+ "uris": ["/*"],
+ "upstream": {
+ "type": "roundrobin",
+ "nodes": []
+ }
+ }`
+
+ // bind struct
+ var route Route
+ err := json.Unmarshal([]byte(routeStr), &route)
+ assert.Nil(t, err)
+
+ // nodes format
+ nodes := NodesFormat(route.Upstream.Nodes)
+
+ // json encode for client
+ res, err := json.Marshal(nodes)
+ assert.Nil(t, err)
+ jsonStr := string(res)
+ assert.Contains(t, jsonStr, `[]`)
+}
+
+func TestNodesFormat_empty_map(t *testing.T) {
+ // route data saved in ETCD
+ routeStr := `{
+ "uris": ["/*"],
+ "upstream": {
+ "type": "roundrobin",
+ "nodes": {}
+ }
+ }`
+
+ // bind struct
+ var route Route
+ err := json.Unmarshal([]byte(routeStr), &route)
+ assert.Nil(t, err)
+
+ // nodes format
+ nodes := NodesFormat(route.Upstream.Nodes)
+
+ // json encode for client
+ res, err := json.Marshal(nodes)
+ assert.Nil(t, err)
+ jsonStr := string(res)
+ assert.Contains(t, jsonStr, `[]`)
+}
+
+func TestNodesFormat_no_nodes(t *testing.T) {
+ // route data saved in ETCD
+ routeStr := `{
+ "uris": ["/*"],
+ "upstream": {
+ "type": "roundrobin",
+ "service_name": "USER-SERVICE",
+ "discovery_type": "eureka"
+ }
+ }`
+
+ // bind struct
+ var route Route
+ err := json.Unmarshal([]byte(routeStr), &route)
+ assert.Nil(t, err)
+
+ // nodes format
+ nodes := NodesFormat(route.Upstream.Nodes)
+
+ // json encode for client
+ res, err := json.Marshal(nodes)
+ assert.Nil(t, err)
+ jsonStr := string(res)
+ assert.Contains(t, jsonStr, `null`)
+}
diff --git a/web/cypress/fixtures/data.json b/web/cypress/fixtures/data.json
index 9c14e98..6412060 100644
--- a/web/cypress/fixtures/data.json
+++ b/web/cypress/fixtures/data.json
@@ -1,6 +1,7 @@
{
"deletePluginSuccess": "Delete Plugin Successfully",
"createUpstreamSuccess": "Create Upstream Successfully",
+ "configureUpstreamSuccess": "Configure Upstream Successfully",
"createServiceSuccess": "Create Service Successfully",
"editServiceSuccess": "Configure Service Successfully",
"deleteServiceSuccess": "Delete Service Successfully",
diff --git a/web/cypress/fixtures/selector.json b/web/cypress/fixtures/selector.json
index f075bc0..d6691b9 100644
--- a/web/cypress/fixtures/selector.json
+++ b/web/cypress/fixtures/selector.json
@@ -30,6 +30,7 @@
"nameSearch": "[title=Name]",
"description": "#desc",
"upstreamSelector": "[data-cy=upstream_selector]",
+ "upstreamNodeMinus0": "[data-cy=upstream-node-minus-0]",
"addHost": "[data-cy=addHost]",
"hosts_1": "#hosts_1",
"remoteHost": "#remote_addrs_0",
diff --git a/web/cypress/integration/upstream/create_and_edit_upstream_with_no_nodes.spec.js b/web/cypress/integration/upstream/create_and_edit_upstream_with_no_nodes.spec.js
new file mode 100644
index 0000000..95cc820
--- /dev/null
+++ b/web/cypress/integration/upstream/create_and_edit_upstream_with_no_nodes.spec.js
@@ -0,0 +1,68 @@
+/*
+ * 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', () => {
+ beforeEach(() => {
+ cy.login();
+
+ cy.fixture('selector.json').as('domSelector');
+ cy.fixture('data.json').as('data');
+ });
+
+ it('should create upstream with no nodes', function () {
+ cy.visit('/');
+ cy.contains('Upstream').click();
+ cy.contains('Create').click();
+
+ cy.get(this.domSelector.name).type(this.data.upstreamName);
+ cy.get(this.domSelector.description).type(this.data.description);
+
+ // delete all nodes
+ cy.get(this.domSelector.upstreamNodeMinus0).click();
+
+ cy.contains('Next').click();
+ cy.contains('Submit').click();
+ cy.get(this.domSelector.notification).should('contain', this.data.createUpstreamSuccess);
+ cy.url().should('contains', 'upstream/list');
+ });
+
+ it('should configure the upstream with no nodes', function () {
+ cy.visit('/');
+ cy.contains('Upstream').click();
+
+ cy.get(this.domSelector.nameSelector).type(this.data.upstreamName);
+ cy.contains('Search').click();
+ cy.contains(this.data.upstreamName).siblings().contains('Configure').click();
+
+ cy.get(this.domSelector.upstreamNodeMinus0).should('not.exist');
+ cy.contains('Next').click();
+ cy.contains('Submit').click();
+
+ cy.get(this.domSelector.notification).should('contain', this.data.configureUpstreamSuccess);
+ cy.url().should('contains', 'upstream/list');
+
+ });
+
+ it('should delete the upstream', function () {
+ cy.visit('/');
+ cy.contains('Upstream').click();
+ cy.contains(this.data.upstreamName).siblings().contains('Delete').click();
+ cy.contains('button', 'Confirm').click();
+ cy.get(this.domSelector.notification).should('contain', this.data.deleteUpstreamSuccess);
+ });
+ });
diff --git a/web/src/components/Upstream/components/Nodes.tsx b/web/src/components/Upstream/components/Nodes.tsx
index 92bfcda..92bfc94 100644
--- a/web/src/components/Upstream/components/Nodes.tsx
+++ b/web/src/components/Upstream/components/Nodes.tsx
@@ -105,7 +105,7 @@ const Component: React.FC<Props> = ({ readonly }) => {
</Col>
<Col style={{ ...removeBtnStyle, marginLeft: -50 }}>
{!readonly && (
- <MinusCircleOutlined onClick={() => remove(field.name)} />
+ <MinusCircleOutlined data-cy={`upstream-node-minus-${index}`} onClick={() => remove(field.name)} />
)}
</Col>
</Row>
diff --git a/web/src/components/Upstream/service.ts b/web/src/components/Upstream/service.ts
index 098ed22..168df8b 100644
--- a/web/src/components/Upstream/service.ts
+++ b/web/src/components/Upstream/service.ts
@@ -68,7 +68,6 @@ export const convertToRequestData = (
type,
hash_on,
key,
- k8s_deployment_info,
nodes,
pass_host,
upstream_host,
@@ -82,14 +81,6 @@ export const convertToRequestData = (
data = omit(data, "upstream_id") as any
- if (nodes && k8s_deployment_info) {
- return undefined;
- }
-
- if (!nodes && !k8s_deployment_info) {
- return undefined;
- }
-
if (type === 'chash') {
if (!hash_on) {
return undefined;
@@ -112,9 +103,13 @@ export const convertToRequestData = (
return undefined
}
+ /**
+ * nodes will be [] or node list
+ * when upstream_id === none, None === undefined
+ */
if (nodes) {
// NOTE: https://github.com/ant-design/ant-design/issues/27396
- data.nodes = data.nodes?.map((item) => {
+ data.nodes = nodes?.map((item) => {
return pick(item, ['host', 'port', 'weight']);
});
return data;
diff --git a/web/src/pages/Route/typing.d.ts b/web/src/pages/Route/typing.d.ts
index 7a8a3e6..3245c7a 100644
--- a/web/src/pages/Route/typing.d.ts
+++ b/web/src/pages/Route/typing.d.ts
@@ -190,7 +190,6 @@ declare namespace RouteModule {
upstream: {
checks: UpstreamModule.HealthCheck;
create_time: number;
- k8s_deployment_info: UpstreamModule.K8SDeploymentInfo;
id: string;
nodes: {
port: number;
diff --git a/web/src/pages/Upstream/locales/en-US.ts b/web/src/pages/Upstream/locales/en-US.ts
index 702104e..23741e5 100644
--- a/web/src/pages/Upstream/locales/en-US.ts
+++ b/web/src/pages/Upstream/locales/en-US.ts
@@ -67,7 +67,7 @@ export default {
'page.upstream.create': 'Create Upstream',
'page.upstream.configure': 'Configure Upstream',
'page.upstream.create.upstream.successfully': 'Create Upstream Successfully',
- 'page.upstream.edit.upstream.successfully': 'Update Upstream Successfully',
+ 'page.upstream.edit.upstream.successfully': 'Configure Upstream Successfully',
'page.upstream.create.basic.info': 'Basic Information',
'page.upstream.create.preview': 'Preview',
diff --git a/web/src/pages/Upstream/typing.d.ts b/web/src/pages/Upstream/typing.d.ts
index a262ba1..7adf150 100644
--- a/web/src/pages/Upstream/typing.d.ts
+++ b/web/src/pages/Upstream/typing.d.ts
@@ -68,7 +68,6 @@ declare namespace UpstreamModule {
upstream_id: string;
type: Type;
nodes?: Node[];
- k8s_deployment_info?: K8SDeploymentInfo;
hash_on?: 'vars' | 'header' | 'cookie' | 'consumer';
key?: string;
checks?: HealthCheck;