You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by ju...@apache.org on 2020/10/19 01:52:57 UTC
[apisix-dashboard] branch refactor updated: feat: compatible with
PUT method of `admin api` and nodes of upstream (#561)
This is an automated email from the ASF dual-hosted git repository.
juzhiyuan pushed a commit to branch refactor
in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git
The following commit(s) were added to refs/heads/refactor by this push:
new 0bbcc75 feat: compatible with PUT method of `admin api` and nodes of upstream (#561)
0bbcc75 is described below
commit 0bbcc7534e0ab570e7d876291301d79e6b5f93bf
Author: nic-chen <33...@users.noreply.github.com>
AuthorDate: Mon Oct 19 09:52:28 2020 +0800
feat: compatible with PUT method of `admin api` and nodes of upstream (#561)
* feat: support labels
* feat: compatible with PUT method of `admin api`
* fix mock test fail
* feat: upstream nodes format
* test: add test case
* fix code style
* fix: update schema sync tool
---
api/build-tools/schema-sync.lua | 1 +
api/internal/core/entity/entity.go | 85 ++++++++++++++++++--------
api/internal/core/entity/format.go | 52 ++++++++++++++++
api/internal/core/entity/format_test.go | 39 ++++++++++++
api/internal/core/store/store.go | 42 ++++++-------
api/internal/core/store/store_test.go | 8 +--
api/internal/handler/consumer/consumer.go | 10 ++-
api/internal/handler/consumer/consumer_test.go | 29 +++++++++
api/internal/handler/route/route.go | 15 ++++-
api/internal/handler/route/route_test.go | 6 +-
api/internal/handler/service/service.go | 15 ++++-
api/internal/handler/ssl/ssl.go | 6 +-
api/internal/handler/upstream/upstream.go | 15 ++++-
13 files changed, 256 insertions(+), 67 deletions(-)
diff --git a/api/build-tools/schema-sync.lua b/api/build-tools/schema-sync.lua
index 596f6d8..257d220 100644
--- a/api/build-tools/schema-sync.lua
+++ b/api/build-tools/schema-sync.lua
@@ -46,6 +46,7 @@ local fake_module_list = {
'resty.openidc',
'resty.random',
'resty.redis',
+ 'resty.rediscluster',
'resty.signal',
'resty.string',
diff --git a/api/internal/core/entity/entity.go b/api/internal/core/entity/entity.go
index 06f4d31..c15a5d5 100644
--- a/api/internal/core/entity/entity.go
+++ b/api/internal/core/entity/entity.go
@@ -16,6 +16,12 @@
*/
package entity
+import (
+ "time"
+
+ "github.com/apisix/manager-api/internal/utils"
+)
+
type BaseInfo struct {
ID string `json:"id"`
CreateTime int64 `json:"create_time"`
@@ -26,6 +32,24 @@ func (info *BaseInfo) GetBaseInfo() *BaseInfo {
return info
}
+func (info *BaseInfo) Creating() {
+ if info.ID == "" {
+ info.ID = utils.GetFlakeUidStr()
+ }
+ info.CreateTime = time.Now().Unix()
+ info.UpdateTime = time.Now().Unix()
+}
+
+func (info *BaseInfo) Updating(storedInfo *BaseInfo) {
+ info.ID = storedInfo.ID
+ info.CreateTime = storedInfo.CreateTime
+ info.UpdateTime = time.Now().Unix()
+}
+
+type BaseInfoSetter interface {
+ GetBaseInfo() *BaseInfo
+}
+
type BaseInfoGetter interface {
GetBaseInfo() *BaseInfo
}
@@ -46,10 +70,11 @@ type Route struct {
FilterFunc string `json:"filter_func,omitempty"`
Script interface{} `json:"script,omitempty"`
Plugins map[string]interface{} `json:"plugins,omitempty"`
- Upstream interface{} `json:"upstream,omitempty"`
+ Upstream *UpstreamDef `json:"upstream,omitempty"`
ServiceID string `json:"service_id,omitempty"`
UpstreamID string `json:"upstream_id,omitempty"`
ServiceProtocol string `json:"service_protocol,omitempty"`
+ Labels map[string]string `json:"labels,omitempty"`
}
// --- structures for upstream start ---
@@ -112,22 +137,27 @@ type HealthChecker struct {
Passive Passive `json:"passive,omitempty"`
}
+type UpstreamDef struct {
+ Nodes interface{} `json:"nodes,omitempty"`
+ Retries int `json:"retries,omitempty"`
+ Timeout interface{} `json:"timeout,omitempty"`
+ K8sInfo interface{} `json:"k8s_deployment_info,omitempty"`
+ Type string `json:"type,omitempty"`
+ Checks interface{} `json:"checks,omitempty"`
+ HashOn string `json:"hash_on,omitempty"`
+ Key string `json:"key,omitempty"`
+ EnableWebsocket bool `json:"enable_websocket,omitempty"`
+ PassHost string `json:"pass_host,omitempty"`
+ UpstreamHost string `json:"upstream_host,omitempty"`
+ Name string `json:"name,omitempty"`
+ Desc string `json:"desc,omitempty"`
+ ServiceName string `json:"service_name,omitempty"`
+ Labels map[string]string `json:"labels,omitempty"`
+}
+
type Upstream struct {
BaseInfo
- Nodes []interface{} `json:"nodes,omitempty"`
- Retries int `json:"retries,omitempty"`
- Timeout interface{} `json:"timeout,omitempty"`
- K8sInfo interface{} `json:"k8s_deployment_info,omitempty"`
- Type string `json:"type,omitempty"`
- Checks interface{} `json:"checks,omitempty"`
- HashOn string `json:"hash_on,omitempty"`
- Key string `json:"key,omitempty"`
- EnableWebsocket bool `json:"enable_websocket,omitempty"`
- PassHost string `json:"pass_host,omitempty"`
- UpstreamHost string `json:"upstream_host,omitempty"`
- Name string `json:"name,omitempty"`
- Desc string `json:"desc,omitempty"`
- ServiceName string `json:"service_name,omitempty"`
+ UpstreamDef
}
type UpstreamNameResponse struct {
@@ -150,30 +180,33 @@ type Consumer struct {
Username string `json:"username"`
Desc string `json:"desc,omitempty"`
Plugins map[string]interface{} `json:"plugins,omitempty"`
+ Labels map[string]string `json:"labels,omitempty"`
}
type SSL struct {
BaseInfo
- Cert string `json:"cert,omitempty"`
- Key string `json:"key,omitempty"`
- Sni string `json:"sni,omitempty"`
- Snis []string `json:"snis,omitempty"`
- Certs []string `json:"certs,omitempty"`
- Keys []string `json:"keys,omitempty"`
- ExpTime int64 `json:"exptime,omitempty"`
- Status int `json:"status"`
- ValidityStart int64 `json:"validity_start,omitempty"`
- ValidityEnd int64 `json:"validity_end,omitempty"`
+ Cert string `json:"cert,omitempty"`
+ Key string `json:"key,omitempty"`
+ Sni string `json:"sni,omitempty"`
+ Snis []string `json:"snis,omitempty"`
+ Certs []string `json:"certs,omitempty"`
+ Keys []string `json:"keys,omitempty"`
+ ExpTime int64 `json:"exptime,omitempty"`
+ Status int `json:"status"`
+ ValidityStart int64 `json:"validity_start,omitempty"`
+ ValidityEnd int64 `json:"validity_end,omitempty"`
+ Labels map[string]string `json:"labels,omitempty"`
}
type Service struct {
BaseInfo
Name string `json:"name,omitempty"`
Desc string `json:"desc,omitempty"`
- Upstream interface{} `json:"upstream,omitempty"`
+ Upstream *UpstreamDef `json:"upstream,omitempty"`
UpstreamID string `json:"upstream_id,omitempty"`
Plugins map[string]interface{} `json:"plugins,omitempty"`
Script string `json:"script,omitempty"`
+ Labels map[string]string `json:"labels,omitempty"`
}
type Script struct {
diff --git a/api/internal/core/entity/format.go b/api/internal/core/entity/format.go
new file mode 100644
index 0000000..0c5d8d1
--- /dev/null
+++ b/api/internal/core/entity/format.go
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+package entity
+
+import (
+ "log"
+ "strconv"
+ "strings"
+)
+
+func NodesFormat(obj interface{}) interface{} {
+ if value, ok := obj.(map[string]float64); ok {
+ var nodes []*Node
+ var strArr []string
+ for key, val := range value {
+ node := &Node{}
+ strArr = strings.Split(key, ":")
+ if len(strArr) != 2 {
+ log.Println("length of string array is not 2")
+ return obj
+ }
+
+ port, err := strconv.Atoi(strArr[1])
+ if err != nil {
+ log.Println("parse int fail:", err)
+ return obj
+ }
+
+ node.Host = strArr[0]
+ node.Port = port
+ node.Weight = int(val)
+ nodes = append(nodes, node)
+ }
+ return nodes
+ }
+
+ return obj
+}
diff --git a/api/internal/core/entity/format_test.go b/api/internal/core/entity/format_test.go
new file mode 100644
index 0000000..cc51bf2
--- /dev/null
+++ b/api/internal/core/entity/format_test.go
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+package entity
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestConsumer(t *testing.T) {
+ nodesStr := `{
+ "127.0.0.1:8080": 1
+ }`
+ nodesMap := map[string]float64{}
+ json.Unmarshal([]byte(nodesStr), &nodesMap)
+ res := NodesFormat(nodesMap)
+ nodes := res.([]*Node)
+
+ assert.Equal(t, 1, len(nodes))
+ assert.Equal(t, "127.0.0.1", nodes[0].Host)
+ assert.Equal(t, 8080, nodes[0].Port)
+ assert.Equal(t, 1, nodes[0].Weight)
+}
diff --git a/api/internal/core/store/store.go b/api/internal/core/store/store.go
index 8a4b71c..b285183 100644
--- a/api/internal/core/store/store.go
+++ b/api/internal/core/store/store.go
@@ -31,14 +31,13 @@ import (
"github.com/apisix/manager-api/internal/core/entity"
"github.com/apisix/manager-api/internal/core/storage"
- "github.com/apisix/manager-api/internal/utils"
)
type Interface interface {
Get(key string) (interface{}, error)
List(input ListInput) (*ListOutput, error)
Create(ctx context.Context, obj interface{}) error
- Update(ctx context.Context, obj interface{}) error
+ Update(ctx context.Context, obj interface{}, createOnFail bool) error
BatchDelete(ctx context.Context, keys []string) error
}
@@ -92,6 +91,9 @@ func (s *GenericStore) Init() error {
return err
}
for i := range ret {
+ if ret[i] == "init_dir" {
+ continue
+ }
objPtr, err := s.StringToObjPtr(ret[i])
if err != nil {
return err
@@ -136,6 +138,7 @@ func (s *GenericStore) Get(key string) (interface{}, error) {
type ListInput struct {
Predicate func(obj interface{}) bool
+ Format func(obj interface{}) interface{}
PageSize int
// start from 1
PageNumber int
@@ -165,6 +168,9 @@ func (s *GenericStore) List(input ListInput) (*ListOutput, error) {
if input.Predicate != nil && !input.Predicate(value) {
return true
}
+ if input.Format != nil {
+ value = input.Format(value)
+ }
ret = append(ret, value)
return true
})
@@ -223,13 +229,9 @@ func (s *GenericStore) ingestValidate(obj interface{}) (err error) {
}
func (s *GenericStore) Create(ctx context.Context, obj interface{}) error {
- if getter, ok := obj.(entity.BaseInfoGetter); ok {
- info := getter.GetBaseInfo()
- if info.ID == "" {
- info.ID = utils.GetFlakeUidStr()
- }
- info.CreateTime = time.Now().Unix()
- info.UpdateTime = time.Now().Unix()
+ if setter, ok := obj.(entity.BaseInfoSetter); ok {
+ info := setter.GetBaseInfo()
+ info.Creating()
}
if err := s.ingestValidate(obj); err != nil {
@@ -256,7 +258,7 @@ func (s *GenericStore) Create(ctx context.Context, obj interface{}) error {
return nil
}
-func (s *GenericStore) Update(ctx context.Context, obj interface{}) error {
+func (s *GenericStore) Update(ctx context.Context, obj interface{}, createIfNotExist bool) error {
if err := s.ingestValidate(obj); err != nil {
return err
}
@@ -265,21 +267,19 @@ func (s *GenericStore) Update(ctx context.Context, obj interface{}) error {
if key == "" {
return fmt.Errorf("key is required")
}
- oldObj, ok := s.cache.Load(key)
+ storedObj, ok := s.cache.Load(key)
if !ok {
+ if createIfNotExist {
+ return s.Create(ctx, obj)
+ }
return fmt.Errorf("key: %s is not found", key)
}
- createTime := int64(0)
- if oldGetter, ok := oldObj.(entity.BaseInfoGetter); ok {
- oldInfo := oldGetter.GetBaseInfo()
- createTime = oldInfo.CreateTime
- }
-
- if getter, ok := obj.(entity.BaseInfoGetter); ok {
- info := getter.GetBaseInfo()
- info.CreateTime = createTime
- info.UpdateTime = time.Now().Unix()
+ if setter, ok := obj.(entity.BaseInfoGetter); ok {
+ storedGetter := storedObj.(entity.BaseInfoGetter)
+ storedInfo := storedGetter.GetBaseInfo()
+ info := setter.GetBaseInfo()
+ info.Updating(storedInfo)
}
bs, err := json.Marshal(obj)
diff --git a/api/internal/core/store/store_test.go b/api/internal/core/store/store_test.go
index f7ad5f4..53f6a85 100644
--- a/api/internal/core/store/store_test.go
+++ b/api/internal/core/store/store_test.go
@@ -624,7 +624,7 @@ func TestGenericStore_Update(t *testing.T) {
Field2: "test2",
},
giveCache: map[string]interface{}{
- "test1": struct{}{},
+ "test1": &TestStruct{},
},
giveStore: &GenericStore{
opt: GenericStoreOption{
@@ -643,7 +643,7 @@ func TestGenericStore_Update(t *testing.T) {
Field2: "test2",
},
giveCache: map[string]interface{}{
- "test1": struct{}{},
+ "test1": &TestStruct{},
},
giveStore: &GenericStore{
opt: GenericStoreOption{
@@ -664,7 +664,7 @@ func TestGenericStore_Update(t *testing.T) {
Field2: "test2",
},
giveCache: map[string]interface{}{
- "test2": struct{}{},
+ "test2": &TestStruct{},
},
giveStore: &GenericStore{
opt: GenericStoreOption{
@@ -704,7 +704,7 @@ func TestGenericStore_Update(t *testing.T) {
tc.giveStore.Stg = mStorage
tc.giveStore.opt.Validator = mValidator
- err := tc.giveStore.Update(context.TODO(), tc.giveObj)
+ err := tc.giveStore.Update(context.TODO(), tc.giveObj, false)
assert.True(t, validateCalled, tc.caseDesc)
if err != nil {
assert.Equal(t, tc.wantErr, err, tc.caseDesc)
diff --git a/api/internal/handler/consumer/consumer.go b/api/internal/handler/consumer/consumer.go
index 1fdf3b3..f00d1d2 100644
--- a/api/internal/handler/consumer/consumer.go
+++ b/api/internal/handler/consumer/consumer.go
@@ -50,6 +50,8 @@ func (h *Handler) ApplyRoute(r *gin.Engine) {
wrapper.InputType(reflect.TypeOf(entity.Consumer{}))))
r.PUT("/apisix/admin/consumers/:username", wgin.Wraps(h.Update,
wrapper.InputType(reflect.TypeOf(UpdateInput{}))))
+ r.PUT("/apisix/admin/consumers", wgin.Wraps(h.Update,
+ wrapper.InputType(reflect.TypeOf(UpdateInput{}))))
r.DELETE("/apisix/admin/consumers/:usernames", wgin.Wraps(h.BatchDelete,
wrapper.InputType(reflect.TypeOf(BatchDelete{}))))
}
@@ -117,10 +119,12 @@ func (h *Handler) Update(c droplet.Context) (interface{}, error) {
if input.ID != "" && input.ID != input.Username {
return nil, fmt.Errorf("consumer's id and username must be a same value")
}
- input.Consumer.Username = input.Username
- input.Consumer.ID = input.Username
+ if input.Username != "" {
+ input.Consumer.Username = input.Username
+ }
+ input.Consumer.ID = input.Consumer.Username
- if err := h.consumerStore.Update(c.Context(), &input.Consumer); err != nil {
+ if err := h.consumerStore.Update(c.Context(), &input.Consumer, true); err != nil {
//if not exists, create
if err.Error() == fmt.Sprintf("key: %s is not found", input.Username) {
if err := h.consumerStore.Create(c.Context(), &input.Consumer); err != nil {
diff --git a/api/internal/handler/consumer/consumer_test.go b/api/internal/handler/consumer/consumer_test.go
index 567c796..22664fa 100644
--- a/api/internal/handler/consumer/consumer_test.go
+++ b/api/internal/handler/consumer/consumer_test.go
@@ -197,4 +197,33 @@ func TestConsumer(t *testing.T) {
_, err = handler.Create(ctx)
assert.NotNil(t, err)
+ //create consumer using Update
+ consumer6 := &UpdateInput{}
+ reqBody = `{
+ "username": "nnn",
+ "plugins": {
+ "limit-count": {
+ "count": 2,
+ "time_window": 60,
+ "rejected_code": 503,
+ "key": "remote_addr"
+ }
+ },
+ "desc": "test description"
+ }`
+ json.Unmarshal([]byte(reqBody), consumer6)
+ ctx.SetInput(consumer6)
+ _, err = handler.Update(ctx)
+ assert.Nil(t, err)
+
+ //sleep
+ time.Sleep(time.Duration(100) * time.Millisecond)
+
+ //delete consumer
+ reqBody = `{"usernames": "nnn"}`
+ json.Unmarshal([]byte(reqBody), inputDel)
+ ctx.SetInput(inputDel)
+ _, err = handler.BatchDelete(ctx)
+ assert.Nil(t, err)
+
}
diff --git a/api/internal/handler/route/route.go b/api/internal/handler/route/route.go
index 0895a6a..adb1954 100644
--- a/api/internal/handler/route/route.go
+++ b/api/internal/handler/route/route.go
@@ -61,8 +61,11 @@ func (h *Handler) ApplyRoute(r *gin.Engine) {
wrapper.InputType(reflect.TypeOf(ListInput{}))))
r.POST("/apisix/admin/routes", wgin.Wraps(h.Create,
wrapper.InputType(reflect.TypeOf(entity.Route{}))))
+ r.PUT("/apisix/admin/routes", wgin.Wraps(h.Update,
+ wrapper.InputType(reflect.TypeOf(UpdateInput{}))))
r.PUT("/apisix/admin/routes/:id", wgin.Wraps(h.Update,
wrapper.InputType(reflect.TypeOf(UpdateInput{}))))
+
r.DELETE("/apisix/admin/routes/:ids", wgin.Wraps(h.BatchDelete,
wrapper.InputType(reflect.TypeOf(BatchDelete{}))))
@@ -88,6 +91,9 @@ func (h *Handler) Get(c droplet.Context) (interface{}, error) {
route.Script = script.(*entity.Script).Script
}
+ //format
+ route.Upstream.Nodes = entity.NodesFormat(route.Upstream.Nodes)
+
return route, nil
}
@@ -129,6 +135,11 @@ func (h *Handler) List(c droplet.Context) (interface{}, error) {
}
return true
},
+ Format: func(obj interface{}) interface{} {
+ route := obj.(*entity.Route)
+ route.Upstream.Nodes = entity.NodesFormat(route.Upstream.Nodes)
+ return route
+ },
PageSize: input.PageSize,
PageNumber: input.PageNumber,
})
@@ -261,7 +272,7 @@ func (h *Handler) Update(c droplet.Context) (interface{}, error) {
return nil, err
}
//save original conf
- if err = h.scriptStore.Update(c.Context(), script); err != nil {
+ if err = h.scriptStore.Update(c.Context(), script, true); err != nil {
//if not exists, create
if err.Error() == fmt.Sprintf("key: %s is not found", script.ID) {
if err := h.scriptStore.Create(c.Context(), script); err != nil {
@@ -273,7 +284,7 @@ func (h *Handler) Update(c droplet.Context) (interface{}, error) {
}
}
- if err := h.routeStore.Update(c.Context(), &input.Route); err != nil {
+ if err := h.routeStore.Update(c.Context(), &input.Route, true); err != nil {
return nil, err
}
diff --git a/api/internal/handler/route/route_test.go b/api/internal/handler/route/route_test.go
index 611dccc..438a058 100644
--- a/api/internal/handler/route/route_test.go
+++ b/api/internal/handler/route/route_test.go
@@ -799,11 +799,7 @@ func TestRoute(t *testing.T) {
"methods": ["PUT", "GET"],
"upstream": {
"type": "roundrobin",
- "nodes": [{
- "host": "www.a.com",
- "port": 80,
- "weight": 1
- }]
+ "nodes": {"www.a.com:80": 1}
}
}`
json.Unmarshal([]byte(reqBody), route3)
diff --git a/api/internal/handler/service/service.go b/api/internal/handler/service/service.go
index 7ee3c0f..deee899 100644
--- a/api/internal/handler/service/service.go
+++ b/api/internal/handler/service/service.go
@@ -48,6 +48,8 @@ func (h *Handler) ApplyRoute(r *gin.Engine) {
wrapper.InputType(reflect.TypeOf(ListInput{}))))
r.POST("/apisix/admin/services", wgin.Wraps(h.Create,
wrapper.InputType(reflect.TypeOf(entity.Service{}))))
+ r.PUT("/apisix/admin/services", wgin.Wraps(h.Update,
+ wrapper.InputType(reflect.TypeOf(UpdateInput{}))))
r.PUT("/apisix/admin/services/:id", wgin.Wraps(h.Update,
wrapper.InputType(reflect.TypeOf(UpdateInput{}))))
r.PATCH("/apisix/admin/services/:id", wgin.Wraps(h.Patch,
@@ -67,6 +69,10 @@ func (h *Handler) Get(c droplet.Context) (interface{}, error) {
if err != nil {
return nil, err
}
+
+ service := r.(*entity.Service)
+ service.Upstream.Nodes = entity.NodesFormat(service.Upstream.Nodes)
+
return r, nil
}
@@ -85,6 +91,11 @@ func (h *Handler) List(c droplet.Context) (interface{}, error) {
}
return true
},
+ Format: func(obj interface{}) interface{} {
+ service := obj.(*entity.Service)
+ service.Upstream.Nodes = entity.NodesFormat(service.Upstream.Nodes)
+ return service
+ },
PageSize: input.PageSize,
PageNumber: input.PageNumber,
})
@@ -114,7 +125,7 @@ func (h *Handler) Update(c droplet.Context) (interface{}, error) {
input := c.Input().(*UpdateInput)
input.Service.ID = input.ID
- if err := h.serviceStore.Update(c.Context(), &input.Service); err != nil {
+ if err := h.serviceStore.Update(c.Context(), &input.Service, true); err != nil {
return nil, err
}
@@ -167,7 +178,7 @@ func (h *Handler) Patch(c droplet.Context) (interface{}, error) {
return nil, err
}
- if err := h.serviceStore.Update(c.Context(), &stored); err != nil {
+ if err := h.serviceStore.Update(c.Context(), &stored, false); err != nil {
return nil, err
}
diff --git a/api/internal/handler/ssl/ssl.go b/api/internal/handler/ssl/ssl.go
index a7f5701..3291846 100644
--- a/api/internal/handler/ssl/ssl.go
+++ b/api/internal/handler/ssl/ssl.go
@@ -55,6 +55,8 @@ func (h *Handler) ApplyRoute(r *gin.Engine) {
wrapper.InputType(reflect.TypeOf(ListInput{}))))
r.POST("/apisix/admin/ssl", wgin.Wraps(h.Create,
wrapper.InputType(reflect.TypeOf(entity.SSL{}))))
+ r.PUT("/apisix/admin/ssl", wgin.Wraps(h.Update,
+ wrapper.InputType(reflect.TypeOf(UpdateInput{}))))
r.PUT("/apisix/admin/ssl/:id", wgin.Wraps(h.Update,
wrapper.InputType(reflect.TypeOf(UpdateInput{}))))
r.PATCH("/apisix/admin/ssl/:id", wgin.Wraps(h.Patch,
@@ -163,7 +165,7 @@ func (h *Handler) Update(c droplet.Context) (interface{}, error) {
}
ssl.ID = input.ID
- if err := h.sslStore.Update(c.Context(), ssl); err != nil {
+ if err := h.sslStore.Update(c.Context(), ssl, true); err != nil {
return nil, err
}
@@ -203,7 +205,7 @@ func (h *Handler) Patch(c droplet.Context) (interface{}, error) {
panic(err)
}
- if err := h.sslStore.Update(c.Context(), &stored); err != nil {
+ if err := h.sslStore.Update(c.Context(), &stored, false); err != nil {
return nil, err
}
diff --git a/api/internal/handler/upstream/upstream.go b/api/internal/handler/upstream/upstream.go
index 5071ff2..d7e3365 100644
--- a/api/internal/handler/upstream/upstream.go
+++ b/api/internal/handler/upstream/upstream.go
@@ -49,6 +49,8 @@ func (h *Handler) ApplyRoute(r *gin.Engine) {
wrapper.InputType(reflect.TypeOf(ListInput{}))))
r.POST("/apisix/admin/upstreams", wgin.Wraps(h.Create,
wrapper.InputType(reflect.TypeOf(entity.Upstream{}))))
+ r.PUT("/apisix/admin/upstreams", wgin.Wraps(h.Update,
+ wrapper.InputType(reflect.TypeOf(UpdateInput{}))))
r.PUT("/apisix/admin/upstreams/:id", wgin.Wraps(h.Update,
wrapper.InputType(reflect.TypeOf(UpdateInput{}))))
r.PATCH("/apisix/admin/upstreams/:id", wgin.Wraps(h.Patch,
@@ -71,6 +73,10 @@ func (h *Handler) Get(c droplet.Context) (interface{}, error) {
if err != nil {
return nil, err
}
+
+ upstream := r.(*entity.Upstream)
+ upstream.Nodes = entity.NodesFormat(upstream.Nodes)
+
return r, nil
}
@@ -89,6 +95,11 @@ func (h *Handler) List(c droplet.Context) (interface{}, error) {
}
return true
},
+ Format: func(obj interface{}) interface{} {
+ upstream := obj.(*entity.Upstream)
+ upstream.Nodes = entity.NodesFormat(upstream.Nodes)
+ return upstream
+ },
PageSize: input.PageSize,
PageNumber: input.PageNumber,
})
@@ -118,7 +129,7 @@ func (h *Handler) Update(c droplet.Context) (interface{}, error) {
input := c.Input().(*UpdateInput)
input.Upstream.ID = input.ID
- if err := h.upstreamStore.Update(c.Context(), &input.Upstream); err != nil {
+ if err := h.upstreamStore.Update(c.Context(), &input.Upstream, true); err != nil {
return nil, err
}
@@ -171,7 +182,7 @@ func (h *Handler) Patch(c droplet.Context) (interface{}, error) {
return nil, err
}
- if err := h.upstreamStore.Update(c.Context(), &stored); err != nil {
+ if err := h.upstreamStore.Update(c.Context(), &stored, false); err != nil {
return nil, err
}