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 <33000667+nic-c...@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 }