This is an automated email from the ASF dual-hosted git repository.

chenjunxu 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 822aa6d  feat: support global rules for Manager API (#1057)
822aa6d is described below

commit 822aa6d1f40c5eb5bfef00030304eb5b619a9eea
Author: nic-chen <[email protected]>
AuthorDate: Mon Dec 28 16:23:03 2020 +0800

    feat: support global rules for Manager API (#1057)
    
    * feat: support global rules
---
 api/internal/core/entity/entity.go                 |   6 +
 api/internal/core/store/storehub.go                |  24 +-
 api/internal/filter/schema.go                      |  11 +-
 api/internal/handler/global_rule/global_rule.go    | 175 +++++++++++
 .../handler/global_rule/global_rule_test.go        | 336 +++++++++++++++++++++
 api/internal/route.go                              |   2 +
 api/test/e2e/base.go                               |   1 -
 api/test/e2e/global_rule_test.go                   | 234 ++++++++++++++
 8 files changed, 781 insertions(+), 8 deletions(-)

diff --git a/api/internal/core/entity/entity.go 
b/api/internal/core/entity/entity.go
index 0e68078..32ed6e0 100644
--- a/api/internal/core/entity/entity.go
+++ b/api/internal/core/entity/entity.go
@@ -235,6 +235,12 @@ type Script struct {
        Script interface{} `json:"script,omitempty"`
 }
 
+// swagger:model GlobalPlugins
+type GlobalPlugins struct {
+       ID      interface{}            `json:"id"`
+       Plugins map[string]interface{} `json:"plugins,omitempty"`
+}
+
 type ServerInfo struct {
        BaseInfo
        LastReportTime int64  `json:"last_report_time,omitempty"`
diff --git a/api/internal/core/store/storehub.go 
b/api/internal/core/store/storehub.go
index fe61953..b1e1acf 100644
--- a/api/internal/core/store/storehub.go
+++ b/api/internal/core/store/storehub.go
@@ -34,6 +34,7 @@ const (
        HubKeySsl        HubKey = "ssl"
        HubKeyUpstream   HubKey = "upstream"
        HubKeyScript     HubKey = "script"
+       HubKeyGlobalRule HubKey = "global_rule"
        HubKeyServerInfo HubKey = `server_info`
 )
 
@@ -42,8 +43,15 @@ var (
 )
 
 func InitStore(key HubKey, opt GenericStoreOption) error {
-       if key == HubKeyConsumer || key == HubKeyRoute ||
-               key == HubKeyService || key == HubKeySsl || key == 
HubKeyUpstream {
+       hubsNeedCheck := map[HubKey]bool{
+               HubKeyConsumer:   true,
+               HubKeyRoute:      true,
+               HubKeySsl:        true,
+               HubKeyService:    true,
+               HubKeyUpstream:   true,
+               HubKeyGlobalRule: true,
+       }
+       if _, ok := hubsNeedCheck[key]; ok {
                validator, err := NewAPISIXJsonSchemaValidator("main." + 
string(key))
                if err != nil {
                        return err
@@ -145,6 +153,18 @@ func InitStores() error {
                return err
        }
 
+       err = InitStore(HubKeyGlobalRule, GenericStoreOption{
+               BasePath: "/apisix/global_rules",
+               ObjType:  reflect.TypeOf(entity.GlobalPlugins{}),
+               KeyFunc: func(obj interface{}) string {
+                       r := obj.(*entity.GlobalPlugins)
+                       return utils.InterfaceToString(r.ID)
+               },
+       })
+       if err != nil {
+               return err
+       }
+
        err = InitStore(HubKeyServerInfo, GenericStoreOption{
                BasePath: "/apisix/data_plane/server_info",
                ObjType:  reflect.TypeOf(entity.ServerInfo{}),
diff --git a/api/internal/filter/schema.go b/api/internal/filter/schema.go
index a2a99ee..e9d50f2 100644
--- a/api/internal/filter/schema.go
+++ b/api/internal/filter/schema.go
@@ -37,11 +37,12 @@ import (
 )
 
 var resources = map[string]string{
-       "routes":    "route",
-       "upstreams": "upstream",
-       "services":  "service",
-       "consumers": "consumer",
-       "ssl":       "ssl",
+       "routes":       "route",
+       "upstreams":    "upstream",
+       "services":     "service",
+       "consumers":    "consumer",
+       "ssl":          "ssl",
+       "global_rules": "global_rule",
 }
 
 const (
diff --git a/api/internal/handler/global_rule/global_rule.go 
b/api/internal/handler/global_rule/global_rule.go
new file mode 100644
index 0000000..81b1877
--- /dev/null
+++ b/api/internal/handler/global_rule/global_rule.go
@@ -0,0 +1,175 @@
+/*
+ * 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 global_rule
+
+import (
+       "encoding/json"
+       "reflect"
+
+       "github.com/gin-gonic/gin"
+       "github.com/shiningrush/droplet"
+       "github.com/shiningrush/droplet/wrapper"
+       wgin "github.com/shiningrush/droplet/wrapper/gin"
+
+       "github.com/apisix/manager-api/internal/core/entity"
+       "github.com/apisix/manager-api/internal/core/store"
+       "github.com/apisix/manager-api/internal/handler"
+       "github.com/apisix/manager-api/internal/utils"
+       "github.com/apisix/manager-api/internal/utils/consts"
+)
+
+type Handler struct {
+       globalRuleStore store.Interface
+}
+
+func NewHandler() (handler.RouteRegister, error) {
+       return &Handler{
+               globalRuleStore: store.GetStore(store.HubKeyGlobalRule),
+       }, nil
+}
+
+func (h *Handler) ApplyRoute(r *gin.Engine) {
+       // global plugins
+       r.GET("/apisix/admin/global_rules/:id", wgin.Wraps(h.Get,
+               wrapper.InputType(reflect.TypeOf(GetInput{}))))
+       r.GET("/apisix/admin/global_rules", wgin.Wraps(h.List,
+               wrapper.InputType(reflect.TypeOf(ListInput{}))))
+       r.PUT("/apisix/admin/global_rules/:id", wgin.Wraps(h.Set,
+               wrapper.InputType(reflect.TypeOf(entity.GlobalPlugins{}))))
+       r.PUT("/apisix/admin/global_rules", wgin.Wraps(h.Set,
+               wrapper.InputType(reflect.TypeOf(entity.GlobalPlugins{}))))
+
+       r.PATCH("/apisix/admin/global_rules/:id", consts.ErrorWrapper(Patch))
+       r.PATCH("/apisix/admin/global_rules/:id/*path", 
consts.ErrorWrapper(Patch))
+
+       r.DELETE("/apisix/admin/global_rules/:id", wgin.Wraps(h.BatchDelete,
+               wrapper.InputType(reflect.TypeOf(BatchDeleteInput{}))))
+}
+
+type GetInput struct {
+       ID string `auto_read:"id,path" validate:"required"`
+}
+
+func (h *Handler) Get(c droplet.Context) (interface{}, error) {
+       input := c.Input().(*GetInput)
+
+       r, err := h.globalRuleStore.Get(input.ID)
+       if err != nil {
+               return handler.SpecCodeResponse(err), err
+       }
+       return r, nil
+}
+
+type ListInput struct {
+       store.Pagination
+}
+
+// swagger:operation GET /apisix/admin/global_rules getGlobalRuleList
+//
+// Return the global rule list according to the specified page number and page 
size.
+//
+// ---
+// produces:
+// - application/json
+// parameters:
+// - name: page
+//   in: query
+//   description: page number
+//   required: false
+//   type: integer
+// - name: page_size
+//   in: query
+//   description: page size
+//   required: false
+//   type: integer
+// responses:
+//   '0':
+//     description: list response
+//     schema:
+//       type: array
+//       items:
+//         "$ref": "#/definitions/GlobalPlugins"
+//   default:
+//     description: unexpected error
+//     schema:
+//       "$ref": "#/definitions/ApiError"
+func (h *Handler) List(c droplet.Context) (interface{}, error) {
+       input := c.Input().(*ListInput)
+
+       ret, err := h.globalRuleStore.List(store.ListInput{
+               PageSize:   input.PageSize,
+               PageNumber: input.PageNumber,
+       })
+       if err != nil {
+               return nil, err
+       }
+
+       return ret, nil
+}
+
+func (h *Handler) Set(c droplet.Context) (interface{}, error) {
+       input := c.Input().(*entity.GlobalPlugins)
+
+       if err := h.globalRuleStore.Create(c.Context(), input); err != nil {
+               return handler.SpecCodeResponse(err), err
+       }
+
+       return nil, nil
+}
+
+func Patch(c *gin.Context) (interface{}, error) {
+       reqBody, _ := c.GetRawData()
+       ID := c.Param("id")
+       subPath := c.Param("path")
+
+       routeStore := store.GetStore(store.HubKeyGlobalRule)
+       stored, err := routeStore.Get(ID)
+       if err != nil {
+               return handler.SpecCodeResponse(err), err
+       }
+
+       res, err := utils.MergePatch(stored, subPath, reqBody)
+       if err != nil {
+               return handler.SpecCodeResponse(err), err
+       }
+
+       var globalRule entity.GlobalPlugins
+       err = json.Unmarshal(res, &globalRule)
+       if err != nil {
+               return handler.SpecCodeResponse(err), err
+       }
+
+       if err := routeStore.Update(c, &globalRule, false); err != nil {
+               return handler.SpecCodeResponse(err), err
+       }
+
+       return nil, nil
+}
+
+type BatchDeleteInput struct {
+       ID string `auto_read:"id,path"`
+}
+
+func (h *Handler) BatchDelete(c droplet.Context) (interface{}, error) {
+       input := c.Input().(*BatchDeleteInput)
+
+       if err := h.globalRuleStore.BatchDelete(c.Context(), 
[]string{input.ID}); err != nil {
+               return handler.SpecCodeResponse(err), err
+       }
+
+       return nil, nil
+}
diff --git a/api/internal/handler/global_rule/global_rule_test.go 
b/api/internal/handler/global_rule/global_rule_test.go
new file mode 100644
index 0000000..62a719d
--- /dev/null
+++ b/api/internal/handler/global_rule/global_rule_test.go
@@ -0,0 +1,336 @@
+/*
+ * 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 global_rule
+
+import (
+       "context"
+       "fmt"
+       "net/http"
+       "net/http/httptest"
+       "testing"
+
+       "github.com/gin-gonic/gin"
+       "github.com/shiningrush/droplet"
+       "github.com/shiningrush/droplet/data"
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/mock"
+
+       "github.com/apisix/manager-api/internal/core/entity"
+       "github.com/apisix/manager-api/internal/core/store"
+)
+
+func performRequest(r http.Handler, method, path string) 
*httptest.ResponseRecorder {
+       req := httptest.NewRequest(method, path, nil)
+       w := httptest.NewRecorder()
+       r.ServeHTTP(w, req)
+       return w
+}
+
+func TestHandler_ApplyRoute(t *testing.T) {
+       mStore := &store.MockInterface{}
+       giveRet := `{
+               "id": "test",
+               "plugins": {
+                       "jwt-auth": {}
+               }
+       }`
+       mStore.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+               assert.Equal(t, "test", args.Get(0))
+       }).Return(giveRet, nil)
+
+       h := Handler{globalRuleStore: mStore}
+       r := gin.New()
+       h.ApplyRoute(r)
+
+       w := performRequest(r, "GET", "/apisix/admin/global_rules/test")
+       assert.Equal(t, 200, w.Code)
+}
+
+func TestHandler_Get(t *testing.T) {
+       tests := []struct {
+               caseDesc   string
+               giveInput  *GetInput
+               giveRet    interface{}
+               giveErr    error
+               wantErr    error
+               wantGetKey string
+               wantRet    interface{}
+       }{
+               {
+                       caseDesc:   "normal",
+                       giveInput:  &GetInput{ID: "test"},
+                       wantGetKey: "test",
+                       giveRet: `{
+                               "id": "test",
+                               "plugins": {
+                                       "jwt-auth": {}
+                               }
+                       }`,
+                       wantRet: `{
+                               "id": "test",
+                               "plugins": {
+                                       "jwt-auth": {}
+                               }
+                       }`,
+               },
+               {
+                       caseDesc:   "store get failed",
+                       giveInput:  &GetInput{ID: "non-existent-key"},
+                       wantGetKey: "non-existent-key",
+                       giveErr:    fmt.Errorf("data not found"),
+                       wantErr:    fmt.Errorf("data not found"),
+                       wantRet: &data.SpecCodeResponse{
+                               StatusCode: http.StatusNotFound,
+                       },
+               },
+       }
+
+       for _, tc := range tests {
+               t.Run(tc.caseDesc, func(t *testing.T) {
+                       getCalled := true
+                       mStore := &store.MockInterface{}
+                       mStore.On("Get", mock.Anything).Run(func(args 
mock.Arguments) {
+                               getCalled = true
+                               assert.Equal(t, tc.wantGetKey, args.Get(0))
+                       }).Return(tc.giveRet, tc.giveErr)
+
+                       h := Handler{globalRuleStore: mStore}
+                       ctx := droplet.NewContext()
+                       ctx.SetInput(tc.giveInput)
+                       ret, err := h.Get(ctx)
+                       assert.True(t, getCalled)
+                       assert.Equal(t, tc.wantRet, ret)
+                       assert.Equal(t, tc.wantErr, err)
+               })
+       }
+}
+
+func TestHandler_List(t *testing.T) {
+       tests := []struct {
+               caseDesc  string
+               giveInput *ListInput
+               giveData  []*entity.GlobalPlugins
+               giveErr   error
+               wantErr   error
+               wantInput store.ListInput
+               wantRet   interface{}
+       }{
+               {
+                       caseDesc: "list all condition",
+                       giveInput: &ListInput{
+                               Pagination: store.Pagination{
+                                       PageSize:   10,
+                                       PageNumber: 1,
+                               },
+                       },
+                       wantInput: store.ListInput{
+                               PageSize:   10,
+                               PageNumber: 1,
+                       },
+                       giveData: []*entity.GlobalPlugins{
+                               {ID: "global-rules-1"},
+                               {ID: "global-rules-2"},
+                               {ID: "global-rules-3"},
+                       },
+                       wantRet: &store.ListOutput{
+                               Rows: []interface{}{
+                                       &entity.GlobalPlugins{ID: 
"global-rules-1"},
+                                       &entity.GlobalPlugins{ID: 
"global-rules-2"},
+                                       &entity.GlobalPlugins{ID: 
"global-rules-3"},
+                               },
+                               TotalSize: 3,
+                       },
+               },
+               {
+                       caseDesc: "store list failed",
+                       giveInput: &ListInput{
+                               Pagination: store.Pagination{
+                                       PageSize:   10,
+                                       PageNumber: 10,
+                               },
+                       },
+                       wantInput: store.ListInput{
+                               PageSize:   10,
+                               PageNumber: 10,
+                       },
+                       giveData: []*entity.GlobalPlugins{},
+                       giveErr:  fmt.Errorf("list failed"),
+                       wantErr:  fmt.Errorf("list failed"),
+               },
+       }
+
+       for _, tc := range tests {
+               t.Run(tc.caseDesc, func(t *testing.T) {
+                       getCalled := true
+                       mStore := &store.MockInterface{}
+                       mStore.On("List", mock.Anything).Run(func(args 
mock.Arguments) {
+                               getCalled = true
+                               input := args.Get(0).(store.ListInput)
+                               assert.Equal(t, tc.wantInput.PageSize, 
input.PageSize)
+                               assert.Equal(t, tc.wantInput.PageNumber, 
input.PageNumber)
+                       }).Return(func(input store.ListInput) *store.ListOutput 
{
+                               var returnData []interface{}
+                               for _, c := range tc.giveData {
+                                       if input.Predicate == nil || 
input.Predicate(c) {
+                                               returnData = append(returnData, 
c)
+                                       }
+                               }
+                               return &store.ListOutput{
+                                       Rows:      returnData,
+                                       TotalSize: len(returnData),
+                               }
+                       }, tc.giveErr)
+
+                       h := Handler{globalRuleStore: mStore}
+                       ctx := droplet.NewContext()
+                       ctx.SetInput(tc.giveInput)
+                       ret, err := h.List(ctx)
+                       assert.True(t, getCalled)
+                       assert.Equal(t, tc.wantRet, ret)
+                       assert.Equal(t, tc.wantErr, err)
+               })
+       }
+}
+
+func TestHandler_Set(t *testing.T) {
+       tests := []struct {
+               caseDesc   string
+               giveInput  *entity.GlobalPlugins
+               giveCtx    context.Context
+               giveErr    error
+               wantErr    error
+               wantInput  *entity.GlobalPlugins
+               wantRet    interface{}
+               wantCalled bool
+       }{
+               {
+                       caseDesc: "normal",
+                       giveInput: &entity.GlobalPlugins{
+                               ID: "name",
+                               Plugins: map[string]interface{}{
+                                       "jwt-auth": map[string]interface{}{},
+                               },
+                       },
+                       giveCtx: context.WithValue(context.Background(), 
"test", "value"),
+                       wantInput: &entity.GlobalPlugins{
+                               ID: "name",
+                               Plugins: map[string]interface{}{
+                                       "jwt-auth": map[string]interface{}{},
+                               },
+                       },
+                       wantRet:    nil,
+                       wantCalled: true,
+               },
+               {
+                       caseDesc: "store create failed",
+                       giveInput: &entity.GlobalPlugins{
+                               ID:      "name",
+                               Plugins: nil,
+                       },
+                       giveErr: fmt.Errorf("create failed"),
+                       wantInput: &entity.GlobalPlugins{
+                               ID:      "name",
+                               Plugins: map[string]interface{}(nil),
+                       },
+                       wantErr: fmt.Errorf("create failed"),
+                       wantRet: &data.SpecCodeResponse{
+                               StatusCode: http.StatusInternalServerError,
+                       },
+                       wantCalled: true,
+               },
+       }
+
+       for _, tc := range tests {
+               t.Run(tc.caseDesc, func(t *testing.T) {
+                       methodCalled := true
+                       mStore := &store.MockInterface{}
+                       mStore.On("Create", mock.Anything, 
mock.Anything).Run(func(args mock.Arguments) {
+                               methodCalled = true
+                               assert.Equal(t, tc.giveCtx, args.Get(0))
+                               assert.Equal(t, tc.wantInput, args.Get(1))
+                       }).Return(tc.giveErr)
+
+                       h := Handler{globalRuleStore: mStore}
+                       ctx := droplet.NewContext()
+                       ctx.SetInput(tc.giveInput)
+                       ctx.SetContext(tc.giveCtx)
+                       ret, err := h.Set(ctx)
+                       assert.Equal(t, tc.wantCalled, methodCalled)
+                       assert.Equal(t, tc.wantRet, ret)
+                       assert.Equal(t, tc.wantErr, err)
+               })
+       }
+}
+
+func TestHandler_BatchDelete(t *testing.T) {
+       tests := []struct {
+               caseDesc  string
+               giveInput *BatchDeleteInput
+               giveCtx   context.Context
+               giveErr   error
+               wantErr   error
+               wantInput []string
+               wantRet   interface{}
+       }{
+               {
+                       caseDesc: "normal",
+                       giveInput: &BatchDeleteInput{
+                               ID: "user1",
+                       },
+                       giveCtx: context.WithValue(context.Background(), 
"test", "value"),
+                       wantInput: []string{
+                               "user1",
+                       },
+               },
+               {
+                       caseDesc: "store delete failed",
+                       giveInput: &BatchDeleteInput{
+                               ID: "user2",
+                       },
+                       giveCtx: context.WithValue(context.Background(), 
"test", "value"),
+                       giveErr: fmt.Errorf("delete failed"),
+                       wantInput: []string{
+                               "user2",
+                       },
+                       wantErr: fmt.Errorf("delete failed"),
+                       wantRet: &data.SpecCodeResponse{
+                               StatusCode: http.StatusInternalServerError,
+                       },
+               },
+       }
+
+       for _, tc := range tests {
+               t.Run(tc.caseDesc, func(t *testing.T) {
+                       methodCalled := true
+                       mStore := &store.MockInterface{}
+                       mStore.On("BatchDelete", mock.Anything, mock.Anything, 
mock.Anything).Run(func(args mock.Arguments) {
+                               methodCalled = true
+                               assert.Equal(t, tc.giveCtx, args.Get(0))
+                               assert.Equal(t, tc.wantInput, args.Get(1))
+                       }).Return(tc.giveErr)
+
+                       h := Handler{globalRuleStore: mStore}
+                       ctx := droplet.NewContext()
+                       ctx.SetInput(tc.giveInput)
+                       ctx.SetContext(tc.giveCtx)
+                       ret, err := h.BatchDelete(ctx)
+                       assert.True(t, methodCalled)
+                       assert.Equal(t, tc.wantErr, err)
+                       assert.Equal(t, tc.wantRet, ret)
+               })
+       }
+}
diff --git a/api/internal/route.go b/api/internal/route.go
index f36e819..02e1650 100644
--- a/api/internal/route.go
+++ b/api/internal/route.go
@@ -31,6 +31,7 @@ import (
        "github.com/apisix/manager-api/internal/handler"
        "github.com/apisix/manager-api/internal/handler/authentication"
        "github.com/apisix/manager-api/internal/handler/consumer"
+       "github.com/apisix/manager-api/internal/handler/global_rule"
        "github.com/apisix/manager-api/internal/handler/healthz"
        "github.com/apisix/manager-api/internal/handler/label"
        "github.com/apisix/manager-api/internal/handler/plugin"
@@ -68,6 +69,7 @@ func SetUpRouter() *gin.Engine {
                plugin.NewHandler,
                healthz.NewHandler,
                authentication.NewHandler,
+               global_rule.NewHandler,
                route_online_debug.NewHandler,
                server_info.NewHandler,
                label.NewHandler,
diff --git a/api/test/e2e/base.go b/api/test/e2e/base.go
index 5c79f85..4bed9cb 100644
--- a/api/test/e2e/base.go
+++ b/api/test/e2e/base.go
@@ -238,5 +238,4 @@ func testCaseCheck(tc HttpTestCase, t *testing.T) {
                        resp.Body().Contains(tc.ExpectBody)
                }
        })
-
 }
diff --git a/api/test/e2e/global_rule_test.go b/api/test/e2e/global_rule_test.go
new file mode 100644
index 0000000..19a59b3
--- /dev/null
+++ b/api/test/e2e/global_rule_test.go
@@ -0,0 +1,234 @@
+/*
+ * 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 e2e
+
+import (
+       "net/http"
+       "testing"
+)
+
+func TestGlobalRule(t *testing.T) {
+       tests := []HttpTestCase{
+               {
+                       Desc:         "make sure the route doesn't exist",
+                       Object:       APISIXExpect(t),
+                       Method:       http.MethodGet,
+                       Path:         "/hello",
+                       ExpectStatus: http.StatusNotFound,
+                       ExpectBody:   `{"error_msg":"404 Route Not Found"}`,
+               },
+               {
+                       Desc:   "create route",
+                       Object: ManagerApiExpect(t),
+                       Method: http.MethodPut,
+                       Path:   "/apisix/admin/routes/r1",
+                       Body: `{
+                                "uri": "/hello",
+                                "upstream": {
+                                        "type": "roundrobin",
+                                       "nodes": [{
+                                               "host": "172.16.238.20",
+                                               "port": 1981,
+                                               "weight": 1
+                                       }]
+                                }
+                        }`,
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+               },
+               {
+                       Desc:   "create global rule",
+                       Object: ManagerApiExpect(t),
+                       Path:   "/apisix/admin/global_rules/1",
+                       Method: http.MethodPut,
+                       Body: `{
+                                "id": "1",
+                                "plugins": {
+                                       "response-rewrite": {
+                                           "headers": {
+                                               "X-VERSION":"1.0"
+                                           }
+                                       },
+                                       "uri-blocker": {
+                                               "block_rules": 
["select.+(from|limit)", "(?:(union(.*?)select))"]
+                                       }
+                                }
+                        }`,
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+               },
+               {
+                       Desc:          "verify route with header",
+                       Object:        APISIXExpect(t),
+                       Method:        http.MethodGet,
+                       Path:          "/hello",
+                       ExpectStatus:  http.StatusOK,
+                       ExpectBody:    "hello world",
+                       ExpectHeaders: map[string]string{"X-VERSION": "1.0"},
+                       Sleep:         sleepTime,
+               },
+               {
+                       Desc:          "verify route that should be blocked",
+                       Object:        APISIXExpect(t),
+                       Method:        http.MethodGet,
+                       Path:          "/hello",
+                       Query:         "name=;select%20from%20sys",
+                       ExpectStatus:  http.StatusForbidden,
+                       ExpectHeaders: map[string]string{"X-VERSION": "1.0"},
+               },
+               {
+                       Desc:   "update route with same plugin 
response-rewrite",
+                       Object: ManagerApiExpect(t),
+                       Method: http.MethodPut,
+                       Path:   "/apisix/admin/routes/r1",
+                       Body: `{
+                               "uri": "/hello",
+                               "plugins": {
+                                       "response-rewrite": {
+                                               "headers": {
+                                                       "X-VERSION":"2.0"
+                                               }
+                                       }
+                               },
+                               "upstream": {
+                                       "type": "roundrobin",
+                                       "nodes": [{
+                                               "host": "172.16.238.20",
+                                               "port": 1981,
+                                               "weight": 1
+                                       }]
+                               }
+                        }`,
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+               },
+               {
+                       Desc:          "verify route that header should be the 
same as the route config",
+                       Object:        APISIXExpect(t),
+                       Method:        http.MethodGet,
+                       Path:          "/hello",
+                       ExpectStatus:  http.StatusOK,
+                       ExpectBody:    "hello world",
+                       ExpectHeaders: map[string]string{"X-VERSION": "2.0"},
+                       Sleep:         sleepTime,
+               },
+               {
+                       Desc:         "the uncovered global plugin should 
works",
+                       Object:       APISIXExpect(t),
+                       Method:       http.MethodGet,
+                       Path:         "/hello",
+                       Query:        "name=;select%20from%20sys",
+                       ExpectStatus: http.StatusForbidden,
+                       //ExpectHeaders: map[string]string{"X-VERSION":"2.0"},
+               },
+               {
+                       Desc:   "route patch to enable key-auth",
+                       Object: ManagerApiExpect(t),
+                       Method: http.MethodPatch,
+                       Path:   "/apisix/admin/global_rules/1/plugins",
+                       Body: `{
+                               "response-rewrite": {
+                                   "headers": {
+                                       "X-VERSION":"1.0"
+                                   }
+                               },
+                               "key-auth": {}
+                       }`,
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+               },
+               {
+                       Desc:         "make sure that patch succeeded",
+                       Object:       APISIXExpect(t),
+                       Method:       http.MethodGet,
+                       Path:         "/hello",
+                       ExpectStatus: http.StatusUnauthorized,
+                       Sleep:        sleepTime,
+               },
+               {
+                       Desc:   "route patch to disable key-auth",
+                       Object: ManagerApiExpect(t),
+                       Method: http.MethodPatch,
+                       Path:   "/apisix/admin/global_rules/1",
+                       Body: `{
+                               "plugins": {
+                                       "key-auth": null
+                               }
+                       }`,
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+               },
+               {
+                       Desc:          "make sure that patch succeeded",
+                       Object:        APISIXExpect(t),
+                       Method:        http.MethodGet,
+                       Path:          "/hello",
+                       ExpectStatus:  http.StatusOK,
+                       ExpectBody:    "hello world",
+                       ExpectHeaders: map[string]string{"X-VERSION": "2.0"},
+                       Sleep:         sleepTime,
+               },
+               {
+                       Desc:         "delete global rule",
+                       Object:       ManagerApiExpect(t),
+                       Method:       http.MethodDelete,
+                       Path:         "/apisix/admin/global_rules/1",
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+               },
+               {
+                       Desc:         "make sure the global rule has been 
deleted",
+                       Object:       ManagerApiExpect(t),
+                       Method:       http.MethodGet,
+                       Path:         "/apisix/admin/global_rules/1",
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusNotFound,
+                       ExpectBody:   `{"code":10001,"message":"data not 
found"`,
+                       Sleep:        sleepTime,
+               },
+               {
+                       Desc:          "verify route that should not be 
blocked",
+                       Object:        APISIXExpect(t),
+                       Method:        http.MethodGet,
+                       Path:          "/hello",
+                       Query:         "name=;select%20from%20sys",
+                       ExpectStatus:  http.StatusOK,
+                       ExpectHeaders: map[string]string{"X-VERSION": "2.0"},
+               },
+               {
+                       Desc:         "delete route",
+                       Object:       ManagerApiExpect(t),
+                       Method:       http.MethodDelete,
+                       Path:         "/apisix/admin/routes/r1",
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+               },
+               {
+                       Desc:         "make sure the route has been deleted",
+                       Object:       APISIXExpect(t),
+                       Method:       http.MethodGet,
+                       Path:         "/hello",
+                       ExpectStatus: http.StatusNotFound,
+                       ExpectBody:   `{"error_msg":"404 Route Not Found"}`,
+                       Sleep:        sleepTime,
+               },
+       }
+
+       for _, tc := range tests {
+               testCaseCheck(tc, t)
+       }
+}

Reply via email to