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 bff19b5 fix(be): Modify the service to use the new JSON patch package
(#1396)
bff19b5 is described below
commit bff19b5336485e4ec62666cf518ec22bfbb6370d
Author: JinChen <[email protected]>
AuthorDate: Sat Jan 30 10:32:14 2021 +0800
fix(be): Modify the service to use the new JSON patch package (#1396)
---
api/internal/handler/service/service.go | 47 ++++++++-------
api/internal/handler/service/service_test.go | 71 +++++++++++++++++++++++
api/test/e2e/service_test.go | 87 ++++++++++++++++++++++++++++
3 files changed, 181 insertions(+), 24 deletions(-)
diff --git a/api/internal/handler/service/service.go
b/api/internal/handler/service/service.go
index 240e416..15c4a76 100644
--- a/api/internal/handler/service/service.go
+++ b/api/internal/handler/service/service.go
@@ -17,12 +17,12 @@
package service
import (
+ "encoding/json"
"fmt"
"net/http"
"reflect"
"strings"
- "github.com/api7/go-jsonpatch"
"github.com/gin-gonic/gin"
"github.com/shiningrush/droplet"
"github.com/shiningrush/droplet/data"
@@ -59,7 +59,9 @@ func (h *Handler) ApplyRoute(r *gin.Engine) {
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,
- wrapper.InputType(reflect.TypeOf(UpdateInput{}))))
+ wrapper.InputType(reflect.TypeOf(PatchInput{}))))
+ r.PATCH("/apisix/admin/services/:id/*path", wgin.Wraps(h.Patch,
+ wrapper.InputType(reflect.TypeOf(PatchInput{}))))
r.DELETE("/apisix/admin/services/:ids", wgin.Wraps(h.BatchDelete,
wrapper.InputType(reflect.TypeOf(BatchDelete{}))))
}
@@ -224,42 +226,39 @@ func (h *Handler) BatchDelete(c droplet.Context)
(interface{}, error) {
return nil, nil
}
+type PatchInput struct {
+ ID string `auto_read:"id,path"`
+ SubPath string `auto_read:"path,path"`
+ Body []byte `auto_read:"@body"`
+}
+
func (h *Handler) Patch(c droplet.Context) (interface{}, error) {
- input := c.Input().(*UpdateInput)
- arr := strings.Split(input.ID, "/")
- var subPath string
- if len(arr) > 1 {
- input.ID = arr[0]
- subPath = arr[1]
- }
+ input := c.Input().(*PatchInput)
+ reqBody := input.Body
+ ID := input.ID
+ subPath := input.SubPath
- stored, err := h.serviceStore.Get(c.Context(), input.ID)
+ stored, err := h.serviceStore.Get(c.Context(), ID)
if err != nil {
return handler.SpecCodeResponse(err), err
}
- var patch jsonpatch.Patch
- if subPath != "" {
- patch = jsonpatch.Patch{
- Operations: []jsonpatch.PatchOperation{
- {Op: jsonpatch.Replace, Path: subPath, Value:
c.Input()},
- },
- }
- } else {
- patch, err = jsonpatch.MakePatch(stored, input.Service)
- if err != nil {
- return handler.SpecCodeResponse(err), err
- }
+ res, err := utils.MergePatch(stored, subPath, reqBody)
+ if err != nil {
+ return handler.SpecCodeResponse(err), err
}
- if err := patch.Apply(&stored); err != nil {
+ var service entity.Service
+ err = json.Unmarshal(res, &service)
+ if err != nil {
return handler.SpecCodeResponse(err), err
}
- ret, err := h.serviceStore.Update(c.Context(), &stored, false)
+ ret, err := h.serviceStore.Update(c.Context(), &service, false)
if err != nil {
return handler.SpecCodeResponse(err), err
}
return ret, nil
}
+
diff --git a/api/internal/handler/service/service_test.go
b/api/internal/handler/service/service_test.go
index 88cd5c3..4f469af 100644
--- a/api/internal/handler/service/service_test.go
+++ b/api/internal/handler/service/service_test.go
@@ -19,6 +19,7 @@ package service
import (
"encoding/json"
+ "strings"
"testing"
"time"
@@ -218,3 +219,73 @@ func TestService(t *testing.T) {
assert.Nil(t, err)
}
+
+func TestService_Patch_Update(t *testing.T) {
+ //create
+ handler := &Handler{
+ serviceStore: store.GetStore(store.HubKeyService),
+ }
+ ctx := droplet.NewContext()
+ service := &entity.Service{}
+ reqBody := `{
+ "id": "3",
+ "name": "testservice",
+ "upstream": {
+ "type": "roundrobin",
+ "nodes": [{
+ "host": "172.16.238.20",
+ "port": 1980,
+ "weight": 1
+ }]
+ }
+ }`
+ err := json.Unmarshal([]byte(reqBody), service)
+ assert.Nil(t, err)
+ ctx.SetInput(service)
+ ret, err := handler.Create(ctx)
+ assert.Nil(t, err)
+ objRet, ok := ret.(*entity.Service)
+ assert.True(t, ok)
+ assert.Equal(t, "3", objRet.ID)
+
+ //sleep
+ time.Sleep(time.Duration(20) * time.Millisecond)
+
+ reqBody1 := `{
+ "id": "3",
+ "name": "testpatch",
+ "upstream": {
+ "type": "roundrobin",
+ "nodes": [{
+ "host": "172.16.238.20",
+ "port": 1981,
+ "weight": 1
+ }]
+ }
+ }`
+ responesBody :=
`"nodes":[{"host":"172.16.238.20","port":1981,"weight":1}],"type":"roundrobin"}`
+
+ input2 := &PatchInput{}
+ input2.ID = "3"
+ input2.SubPath = ""
+ input2.Body = []byte(reqBody1)
+ ctx.SetInput(input2)
+
+ ret2, err := handler.Patch(ctx)
+ assert.Nil(t, err)
+ _ret2, err := json.Marshal(ret2)
+ assert.Nil(t, err)
+ isContains := strings.Contains(string(_ret2), responesBody)
+ assert.True(t, isContains)
+
+ //delete test data
+ inputDel2 := &BatchDelete{}
+ reqBody = `{"ids": "3"}`
+ err = json.Unmarshal([]byte(reqBody), inputDel2)
+ assert.Nil(t, err)
+ ctx.SetInput(inputDel2)
+ _, err = handler.BatchDelete(ctx)
+ assert.Nil(t, err)
+
+}
+
diff --git a/api/test/e2e/service_test.go b/api/test/e2e/service_test.go
index 4d9ebe2..9a0cc43 100644
--- a/api/test/e2e/service_test.go
+++ b/api/test/e2e/service_test.go
@@ -269,3 +269,90 @@ func TestService_Teardown(t *testing.T) {
testCaseCheck(tc, t)
}
}
+
+func TestService_Update_Use_Patch_Method(t *testing.T) {
+ tests := []HttpTestCase{
+ {
+ Desc: "create service without plugin",
+ Object: ManagerApiExpect(t),
+ Method: http.MethodPut,
+ Path: "/apisix/admin/services/s5",
+ Headers: map[string]string{"Authorization": token},
+ Body: `{
+ "name": "testservice",
+ "upstream": {
+ "type": "roundrobin",
+ "nodes": [{
+ "host": "172.16.238.20",
+ "port": 1980,
+ "weight": 1
+ }]
+ }
+ }`,
+ ExpectStatus: http.StatusOK,
+ ExpectBody:
"\"name\":\"testservice\",\"upstream\":{\"nodes\":[{\"host\":\"172.16.238.20\",\"port\":1980,\"weight\":1}],\"type\":\"roundrobin\"}}",
+ },
+ {
+ Desc: "update service use patch method",
+ Object: ManagerApiExpect(t),
+ Method: http.MethodPatch,
+ Path: "/apisix/admin/services/s5",
+ Body: `{
+ "name": "testpatch",
+ "upstream": {
+ "type": "roundrobin",
+ "nodes": [{
+ "host": "172.16.238.20",
+ "port": 1981,
+ "weight": 1
+ }]
+ }
+ }`,
+ Headers: map[string]string{"Authorization": token},
+ ExpectStatus: http.StatusOK,
+ },
+ {
+ Desc: "get the service s5",
+ Object: ManagerApiExpect(t),
+ Method: http.MethodGet,
+ Path: "/apisix/admin/services/s5",
+ Headers: map[string]string{"Authorization": token},
+ ExpectCode: http.StatusOK,
+ ExpectBody:
"\"name\":\"testpatch\",\"upstream\":{\"nodes\":[{\"host\":\"172.16.238.20\",\"port\":1981,\"weight\":1}],\"type\":\"roundrobin\"}}",
+ },
+ {
+ Desc: "Update service using path parameter patch
method",
+ Object: ManagerApiExpect(t),
+ Method: http.MethodPatch,
+ Path: "/apisix/admin/services/s5/upstream",
+ Body:
`{"type":"roundrobin","nodes":[{"host":"172.16.238.20","port":1980,"weight":1}]}`,
+ Headers: map[string]string{
+ "Authorization": token,
+ "Content-Type": "text/plain",
+ },
+ ExpectStatus: http.StatusOK,
+ },
+ {
+ Desc: "get service data",
+ Object: ManagerApiExpect(t),
+ Method: http.MethodGet,
+ Path: "/apisix/admin/services/s5",
+ Headers: map[string]string{"Authorization": token},
+ ExpectStatus: http.StatusOK,
+ ExpectBody:
"\"name\":\"testpatch\",\"upstream\":{\"nodes\":[{\"host\":\"172.16.238.20\",\"port\":1980,\"weight\":1}],\"type\":\"roundrobin\"}}",
+ },
+ {
+ Desc: "delete service",
+ Object: ManagerApiExpect(t),
+ Method: http.MethodDelete,
+ Path: "/apisix/admin/services/s5",
+ Headers: map[string]string{"Authorization": token},
+ ExpectStatus: http.StatusOK,
+ },
+ }
+
+ for _, tc := range tests {
+ testCaseCheck(tc, t)
+ }
+}
+