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

juzhiyuan 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 316633b  Feat: manage api  support route group (#432)
316633b is described below

commit 316633b76638192709daf7e17de59b090fc43515
Author: liuxiran <[email protected]>
AuthorDate: Mon Sep 7 17:27:47 2020 +0800

    Feat: manage api  support route group (#432)
    
    * feat(manage-api): add new module route group
    
    * feat(manager-api): add route suport routeGroup
    
    * fix: update some code and lint errors
    
    * fix: support to create new routeGroup while creating route
    
    Co-authored-by: liuxiran <[email protected]>
---
 api/errno/error.go            |   8 ++
 api/route/base.go             |  15 +--
 api/route/route.go            |  51 +++++++++-
 api/route/route_group.go      | 217 ++++++++++++++++++++++++++++++++++++++++++
 api/route/route_group_test.go |  45 +++++++++
 api/script/db/schema.sql      |  14 ++-
 api/service/route.go          |  58 +++++++----
 api/service/route_group.go    | 137 ++++++++++++++++++++++++++
 8 files changed, 517 insertions(+), 28 deletions(-)

diff --git a/api/errno/error.go b/api/errno/error.go
index 93180d8..68f313d 100644
--- a/api/errno/error.go
+++ b/api/errno/error.go
@@ -86,6 +86,14 @@ var (
        ApisixConsumerDeleteError = Message{"010704", "APISIX Consumer delete 
failed", 500}
        DuplicateUserName         = Message{"010705", "Duplicate consumer 
username", 400}
 
+       // 08 routeGroup
+       RouteGroupRequestError      = Message{"010801", "RouteGroup request 
parameters exception: %s", 400}
+       DBRouteGroupError           = Message{"010802", "RouteGroup storage 
failure: %s", 500}
+       DBRouteGroupDeleteError     = Message{"010803", "RouteGroup storage 
delete failed: %s", 500}
+       RouteGroupHasRoutesError    = Message{"010804", "Route exist in this 
route group ", 500}
+       RouteGroupSelectRoutesError = Message{"010805", "RouteGroup select 
routes failed : %s", 500}
+       DuplicateRouteGroupName     = Message{"010806", "RouteGroup name is 
duplicate : %s", 500}
+
        // 99 authentication
        AuthenticationUserError = Message{"019901", "username or password 
error", 401}
 )
diff --git a/api/route/base.go b/api/route/base.go
index 1b48d4a..41f5a1c 100644
--- a/api/route/base.go
+++ b/api/route/base.go
@@ -17,13 +17,13 @@
 package route
 
 import (
-  "github.com/gin-contrib/pprof"
-  "github.com/gin-contrib/sessions"
-  "github.com/gin-contrib/sessions/cookie"
-  "github.com/gin-gonic/gin"
+       "github.com/gin-contrib/pprof"
+       "github.com/gin-contrib/sessions"
+       "github.com/gin-contrib/sessions/cookie"
+       "github.com/gin-gonic/gin"
 
-  "github.com/apisix/manager-api/conf"
-  "github.com/apisix/manager-api/filter"
+       "github.com/apisix/manager-api/conf"
+       "github.com/apisix/manager-api/filter"
 )
 
 func SetUpRouter() *gin.Engine {
@@ -35,7 +35,7 @@ func SetUpRouter() *gin.Engine {
        r := gin.New()
        store := cookie.NewStore([]byte("secret"))
        r.Use(sessions.Sessions("session", store))
-       r.Use(filter.CORS(), filter.Authentication(),filter.RequestId(), 
filter.RequestLogHandler(), filter.RecoverHandler())
+       r.Use(filter.CORS(), filter.Authentication(), filter.RequestId(), 
filter.RequestLogHandler(), filter.RecoverHandler())
 
        AppendHealthCheck(r)
        AppendAuthentication(r)
@@ -44,6 +44,7 @@ func SetUpRouter() *gin.Engine {
        AppendPlugin(r)
        AppendUpstream(r)
        AppendConsumer(r)
+       AppendRouteGroup(r)
 
        pprof.Register(r)
 
diff --git a/api/route/route.go b/api/route/route.go
index c86b437..b6ace35 100644
--- a/api/route/route.go
+++ b/api/route/route.go
@@ -20,6 +20,7 @@ import (
        "encoding/json"
        "net/http"
        "strconv"
+       "strings"
 
        "github.com/apisix/manager-api/conf"
        "github.com/apisix/manager-api/errno"
@@ -101,11 +102,19 @@ func listRoute(c *gin.Context) {
                db = db.Where("upstream_nodes like ? ", "%"+ip+"%")
                isSearch = false
        }
+       if rgid, exist := c.GetQuery("route_group_id"); exist {
+               db = db.Where("route_group_id = ?", rgid)
+               isSearch = false
+       }
+       if rgName, exist := c.GetQuery("route_group_name"); exist {
+               db = db.Where("route_group_name like ?", "%"+rgName+"%")
+               isSearch = false
+       }
        // search
        if isSearch {
                if search, exist := c.GetQuery("search"); exist {
                        s := "%" + search + "%"
-                       db = db.Where("name like ? or description like ? or 
hosts like ? or uris like ? or upstream_nodes like ? ", s, s, s, s, s)
+                       db = db.Where("name like ? or description like ? or 
hosts like ? or uris like ? or upstream_nodes like ? or route_group_id = ? or 
route_group_name like ?", s, s, s, s, s, search, s)
                }
        }
        // mysql
@@ -190,6 +199,15 @@ func updateRoute(c *gin.Context) {
                c.AbortWithStatusJSON(http.StatusBadRequest, e.Response())
                return
        }
+       routeGroup := &service.RouteGroupDao{}
+       isCreateGroup := false
+       if len(strings.Trim(routeRequest.RouteGroupId, "")) == 0 {
+               isCreateGroup = true
+               routeGroup.ID = uuid.NewV4()
+               // create route group
+               routeGroup.Name = routeRequest.RouteGroupName
+               routeRequest.RouteGroupId = routeGroup.ID.String()
+       }
        logger.Info(routeRequest.Plugins)
        db := conf.DB()
        arr := service.ToApisixRequest(routeRequest)
@@ -205,6 +223,15 @@ func updateRoute(c *gin.Context) {
                        }
                }()
                logger.Info(rd)
+               if isCreateGroup {
+                       if err := 
tx.Model(&service.RouteGroupDao{}).Create(routeGroup).Error; err != nil {
+                               tx.Rollback()
+                               e := 
errno.FromMessage(errno.DuplicateRouteGroupName, routeGroup.Name)
+                               logger.Error(e.Msg)
+                               
c.AbortWithStatusJSON(http.StatusInternalServerError, e.Response())
+                               return
+                       }
+               }
                if err := tx.Model(&service.Route{}).Update(rd).Error; err != 
nil {
                        // rollback
                        tx.Rollback()
@@ -288,6 +315,8 @@ func findRoute(c *gin.Context) {
                        }
                        result.Script = script
 
+                       result.RouteGroupId = route.RouteGroupId
+                       result.RouteGroupName = route.RouteGroupName
                        resp, _ := json.Marshal(result)
                        c.Data(http.StatusOK, service.ContentType, resp)
                }
@@ -311,6 +340,15 @@ func createRoute(c *gin.Context) {
                c.AbortWithStatusJSON(http.StatusBadRequest, e.Response())
                return
        }
+       routeGroup := &service.RouteGroupDao{}
+       isCreateGroup := false
+       if len(strings.Trim(routeRequest.RouteGroupId, "")) == 0 {
+               isCreateGroup = true
+               routeGroup.ID = uuid.NewV4()
+               // create route group
+               routeGroup.Name = routeRequest.RouteGroupName
+               routeRequest.RouteGroupId = routeGroup.ID.String()
+       }
        logger.Info(routeRequest.Plugins)
        db := conf.DB()
        arr := service.ToApisixRequest(routeRequest)
@@ -326,7 +364,16 @@ func createRoute(c *gin.Context) {
                        }
                }()
                logger.Info(rd)
-               if err := tx.Create(rd).Error; err != nil {
+               if isCreateGroup {
+                       if err := 
tx.Model(&service.RouteGroupDao{}).Create(routeGroup).Error; err != nil {
+                               tx.Rollback()
+                               e := 
errno.FromMessage(errno.DuplicateRouteGroupName, routeGroup.Name)
+                               logger.Error(e.Msg)
+                               
c.AbortWithStatusJSON(http.StatusInternalServerError, e.Response())
+                               return
+                       }
+               }
+               if err := tx.Model(&service.Route{}).Create(rd).Error; err != 
nil {
                        // rollback
                        tx.Rollback()
                        e := errno.FromMessage(errno.DBRouteCreateError, 
err.Error())
diff --git a/api/route/route_group.go b/api/route/route_group.go
new file mode 100644
index 0000000..bfe138a
--- /dev/null
+++ b/api/route/route_group.go
@@ -0,0 +1,217 @@
+package route
+
+import (
+       "encoding/json"
+       "fmt"
+       "net/http"
+       "strconv"
+
+       "github.com/apisix/manager-api/conf"
+       "github.com/apisix/manager-api/errno"
+       "github.com/apisix/manager-api/service"
+       "github.com/gin-gonic/gin"
+       uuid "github.com/satori/go.uuid"
+)
+
+func AppendRouteGroup(r *gin.Engine) *gin.Engine {
+       r.POST("/apisix/admin/routegroups", createRouteGroup)
+       r.GET("/apisix/admin/routegroups/:gid", findRouteGroup)
+       r.GET("/apisix/admin/routegroups", listRouteGroup)
+       r.GET("/apisix/admin/names/routegroups", listRouteGroupName)
+       r.PUT("/apisix/admin/routegroups/:gid", updateRouteGroup)
+       r.DELETE("/apisix/admin/routegroups/:gid", deleteRouteGroup)
+       r.GET("/apisix/admin/notexist/routegroups", isRouteGroupExist)
+       return r
+}
+
+func isRouteGroupExist(c *gin.Context) {
+       if name, exist := c.GetQuery("name"); exist {
+               db := conf.DB()
+               db = db.Table("route_group")
+               exclude, exist := c.GetQuery("exclude")
+               if exist {
+                       db = db.Where("name=? and id<>?", name, exclude)
+               } else {
+                       db = db.Where("name=?", name)
+               }
+               var count int
+               if err := db.Count(&count).Error; err != nil {
+                       e := errno.FromMessage(errno.RouteGroupRequestError, 
err.Error())
+                       logger.Error(e.Msg)
+                       c.AbortWithStatusJSON(http.StatusInternalServerError, 
e.Response())
+                       return
+               } else {
+                       if count == 0 {
+                               c.Data(http.StatusOK, service.ContentType, 
errno.Success())
+                               return
+                       } else {
+                               e := 
errno.FromMessage(errno.DuplicateRouteGroupName, name)
+                               c.AbortWithStatusJSON(http.StatusBadRequest, 
e.Response())
+                               return
+                       }
+               }
+       } else {
+               e := errno.FromMessage(errno.RouteGroupRequestError, "name is 
needed")
+               c.AbortWithStatusJSON(http.StatusBadRequest, e.Response())
+               return
+       }
+}
+
+func createRouteGroup(c *gin.Context) {
+       u4 := uuid.NewV4()
+       gid := u4.String()
+       // todo 参数校验
+       param, exist := c.Get("requestBody")
+       if !exist || len(param.([]byte)) < 1 {
+               e := errno.FromMessage(errno.RouteRequestError, "route_group 
create with no post data")
+               logger.Error(e.Msg)
+               c.AbortWithStatusJSON(http.StatusBadRequest, e.Response())
+               return
+       }
+       // trans params
+       rr := &service.RouteGroupRequest{}
+       if err := rr.Parse(param); err != nil {
+               e := errno.FromMessage(errno.RouteGroupRequestError, 
err.Error())
+               logger.Error(e.Msg)
+               c.AbortWithStatusJSON(http.StatusBadRequest, e.Response())
+               return
+       }
+       rr.Id = gid
+       fmt.Println(rr)
+       // mysql
+       if rd, err := service.Trans2RouteGroupDao(rr); err != nil {
+               c.AbortWithStatusJSON(http.StatusInternalServerError, 
err.Response())
+               return
+       } else {
+               if err := rd.CreateRouteGroup(); err != nil {
+                       e := errno.FromMessage(errno.DBRouteGroupError, 
err.Error())
+                       logger.Error(e.Msg)
+                       c.AbortWithStatusJSON(http.StatusInternalServerError, 
e.Response())
+                       return
+               }
+       }
+       c.Data(http.StatusOK, service.ContentType, errno.Success())
+}
+
+func findRouteGroup(c *gin.Context) {
+       gid := c.Param("gid")
+       routeGroup := &service.RouteGroupDao{}
+       if err, count := routeGroup.FindRouteGroup(gid); err != nil {
+               e := errno.FromMessage(errno.RouteGroupRequestError, 
err.Error()+"route_group ID:"+gid)
+               logger.Error(e.Msg)
+               c.AbortWithStatusJSON(http.StatusBadRequest, e.Response())
+               return
+       } else {
+               if count < 1 {
+                       e := errno.FromMessage(errno.RouteRequestError, " 
route_group ID: "+gid+" not exist")
+                       logger.Error(e.Msg)
+                       c.AbortWithStatusJSON(e.Status, e.Response())
+                       return
+               }
+       }
+       resp, _ := json.Marshal(routeGroup)
+       c.Data(http.StatusOK, service.ContentType, resp)
+}
+
+func listRouteGroup(c *gin.Context) {
+       size, _ := strconv.Atoi(c.Query("size"))
+       page, _ := strconv.Atoi(c.Query("page"))
+       if size == 0 {
+               size = 10
+       }
+       var s string
+
+       rd := &service.RouteGroupDao{}
+       routeGroupList := []service.RouteGroupDao{}
+       if search, exist := c.GetQuery("search"); exist && len(search) > 0 {
+               s = "%" + search + "%"
+       }
+       if err, count := rd.GetRouteGroupList(&routeGroupList, s, page, size); 
err != nil {
+               e := errno.FromMessage(errno.RouteGroupRequestError, 
err.Error())
+               logger.Error(e.Msg)
+               c.AbortWithStatusJSON(http.StatusInternalServerError, 
e.Response())
+               return
+       } else {
+               result := &service.ListResponse{Count: count, Data: 
routeGroupList}
+               resp, _ := json.Marshal(result)
+               c.Data(http.StatusOK, service.ContentType, resp)
+       }
+}
+
+func listRouteGroupName(c *gin.Context) {
+       db := conf.DB()
+       routeGroupList := []service.RouteGroupDao{}
+       var count int
+       if err := 
db.Order("name").Table("route_group").Find(&routeGroupList).Count(&count).Error;
 err != nil {
+               e := errno.FromMessage(errno.RouteGroupRequestError, 
err.Error())
+               logger.Error(e.Msg)
+               c.AbortWithStatusJSON(http.StatusInternalServerError, 
e.Response())
+               return
+       } else {
+               responseList := make([]*service.RouteGroupNameResponse, 0)
+               for _, r := range routeGroupList {
+                       response, err := r.Parse2NameResponse()
+                       if err == nil {
+                               responseList = append(responseList, response)
+                       }
+               }
+               result := &service.ListResponse{Count: count, Data: 
responseList}
+               resp, _ := json.Marshal(result)
+               c.Data(http.StatusOK, service.ContentType, resp)
+       }
+}
+
+func updateRouteGroup(c *gin.Context) {
+       // get params
+       gid := c.Param("gid")
+       param, exist := c.Get("requestBody")
+       if !exist || len(param.([]byte)) < 1 {
+               e := errno.FromMessage(errno.RouteRequestError, "route_group 
update with no post data")
+               logger.Error(e.Msg)
+               c.AbortWithStatusJSON(http.StatusBadRequest, e.Response())
+               return
+       }
+       // trans params
+       rr := &service.RouteGroupRequest{}
+       if err := rr.Parse(param); err != nil {
+               e := errno.FromMessage(errno.RouteGroupRequestError, 
err.Error())
+               logger.Error(e.Msg)
+               c.AbortWithStatusJSON(http.StatusBadRequest, e.Response())
+               return
+       }
+       rr.Id = gid
+       if ud, err := service.Trans2RouteGroupDao(rr); err != nil {
+               c.AbortWithStatusJSON(http.StatusInternalServerError, 
err.Response())
+               return
+       } else {
+               // mysql
+               if err2 := ud.UpdateRouteGroup(); err2 != nil {
+                       e := errno.FromMessage(errno.DBRouteGroupError, 
err2.Error())
+                       logger.Error(e.Msg)
+                       c.AbortWithStatusJSON(http.StatusInternalServerError, 
e.Response())
+                       return
+               }
+       }
+       c.Data(http.StatusOK, service.ContentType, errno.Success())
+}
+
+func deleteRouteGroup(c *gin.Context) {
+       gid := c.Param("gid")
+       // 参数校验
+       routeGroup := &service.RouteGroupDao{}
+       if err := conf.DB().Table("route_group").Where("id=?", 
gid).First(&routeGroup).Error; err != nil {
+               e := errno.FromMessage(errno.RouteGroupRequestError, 
err.Error()+" route_group ID: "+gid)
+               logger.Error(e.Msg)
+               c.AbortWithStatusJSON(http.StatusBadRequest, e.Response())
+               return
+       }
+       // delete from mysql
+       routeGroup.ID = uuid.FromStringOrNil(gid)
+       if err := routeGroup.DeleteRouteGroup(); err != nil {
+               e := errno.FromMessage(errno.DBRouteGroupDeleteError, 
err.Error())
+               logger.Error(e.Msg)
+               c.AbortWithStatusJSON(http.StatusInternalServerError, 
e.Response())
+               return
+       }
+       c.Data(http.StatusOK, service.ContentType, errno.Success())
+}
diff --git a/api/route/route_group_test.go b/api/route/route_group_test.go
new file mode 100644
index 0000000..0500c8f
--- /dev/null
+++ b/api/route/route_group_test.go
@@ -0,0 +1,45 @@
+package route
+
+import (
+       "net/http"
+       "testing"
+)
+
+func TestRouteGroupCurd(t *testing.T) {
+       // create ok
+       handler.
+               Post(uriPrefix+"/routegroups").
+               Header("Authorization", token).
+               JSON(`{
+                       "name": "routegroup_test",
+                       "description": "test description"
+               }`).
+               Expect(t).
+               Status(http.StatusOK).
+               End()
+
+       //c1, _ := service.GetConsumerByUserName("e2e_test_consumer1")
+       id := "8954a39b-330e-4b85-89f5-d1bbfd785b5b"
+       //update ok
+       handler.
+               Put(uriPrefix+"/routegroups/"+id).
+               Header("Authorization", token).
+               JSON(`{
+                       "name": "routegroup_test2",
+                       "description": "test description"
+               }`).
+               Expect(t).
+               Status(http.StatusOK).
+               End()
+       // duplicate username
+       handler.
+               Post(uriPrefix+"/routegroups").
+               Header("Authorization", token).
+               JSON(`{
+                       "name": "routegroup_test",
+                       "description": "test description"
+               }`).
+               Expect(t).
+               Status(http.StatusInternalServerError).
+               End()
+}
diff --git a/api/script/db/schema.sql b/api/script/db/schema.sql
index b1db5a0..af83929 100644
--- a/api/script/db/schema.sql
+++ b/api/script/db/schema.sql
@@ -16,6 +16,8 @@ CREATE TABLE `routes` (
   `content_admin_api` text,
   `create_time` bigint(20),
   `update_time` bigint(20),
+  `route_group_id` varchar(64) NOT NULL,
+  `route_group_name` varchar(64) NOT NULL,
 
   PRIMARY KEY (`id`)
 ) DEFAULT CHARSET=utf8;
@@ -56,4 +58,14 @@ CREATE TABLE `consumers` (
   `update_time` int(10) unsigned NOT NULL,
   PRIMARY KEY (`id`),
   UNIQUE KEY `uni_username` (`username`)
-) DEFAULT CHARSET=utf8;
\ No newline at end of file
+) DEFAULT CHARSET=utf8;
+-- route_group
+CREATE TABLE `route_group` (
+  `id` varchar(64) NOT NULL unique,
+  `name` varchar(200) NOT NULL unique,
+  `description` varchar(200) DEFAULT NULL,
+  `create_time` bigint(20),
+  `update_time` bigint(20),
+
+  PRIMARY KEY (`id`)
+) DEFAULT CHARSET=utf8;
diff --git a/api/service/route.go b/api/service/route.go
index 88cc16b..5a88af9 100644
--- a/api/service/route.go
+++ b/api/service/route.go
@@ -21,6 +21,7 @@ import (
        "fmt"
        "io/ioutil"
        "os/exec"
+       "strings"
        "time"
 
        "github.com/apisix/manager-api/conf"
@@ -53,6 +54,13 @@ func (r *RouteRequest) Parse(body interface{}) error {
                if r.Uris == nil || len(r.Uris) < 1 {
                        r.Uris = []string{"/*"}
                }
+               if len(strings.Trim(r.RouteGroupId, "")) > 0 {
+                       routeGroup := &RouteGroupDao{}
+                       if err, _ := routeGroup.FindRouteGroup(r.RouteGroupId); 
err != nil {
+                               return err
+                       }
+                       r.RouteGroupName = routeGroup.Name
+               }
        }
        return nil
 }
@@ -72,6 +80,8 @@ func (rd *Route) Parse(r *RouteRequest, arr 
*ApisixRouteRequest) error {
        //rd.Name = arr.Name
        rd.Description = arr.Desc
        rd.UpstreamId = r.UpstreamId
+       rd.RouteGroupId = r.RouteGroupId
+       rd.RouteGroupName = r.RouteGroupName
        if content, err := json.Marshal(r); err != nil {
                return err
        } else {
@@ -180,6 +190,8 @@ type RouteRequest struct {
        UpstreamHeader   map[string]string      
`json:"upstream_header,omitempty"`
        Plugins          map[string]interface{} `json:"plugins"`
        Script           map[string]interface{} `json:"script"`
+       RouteGroupId     string                 `json:"route_group_id"`
+       RouteGroupName   string                 `json:"route_group_name"`
 }
 
 func (r *ApisixRouteResponse) Parse() (*RouteRequest, error) {
@@ -277,6 +289,7 @@ func (r *ApisixRouteResponse) Parse() (*RouteRequest, 
error) {
                Redirect:         redirect,
                Upstream:         o.Upstream,
                UpstreamId:       o.UpstreamId,
+               RouteGroupId:     o.RouteGroupId,
                UpstreamProtocol: upstreamProtocol,
                UpstreamPath:     upstreamPath,
                UpstreamHeader:   upstreamHeader,
@@ -388,17 +401,19 @@ type Node struct {
 }
 
 type Value struct {
-       Id         string                 `json:"id"`
-       Name       string                 `json:"name"`
-       Desc       string                 `json:"desc,omitempty"`
-       Priority   int64                  `json:"priority"`
-       Methods    []string               `json:"methods"`
-       Uris       []string               `json:"uris"`
-       Hosts      []string               `json:"hosts"`
-       Vars       [][]string             `json:"vars"`
-       Upstream   *Upstream              `json:"upstream,omitempty"`
-       UpstreamId string                 `json:"upstream_id,omitempty"`
-       Plugins    map[string]interface{} `json:"plugins"`
+       Id             string                 `json:"id"`
+       Name           string                 `json:"name"`
+       Desc           string                 `json:"desc,omitempty"`
+       Priority       int64                  `json:"priority"`
+       Methods        []string               `json:"methods"`
+       Uris           []string               `json:"uris"`
+       Hosts          []string               `json:"hosts"`
+       Vars           [][]string             `json:"vars"`
+       Upstream       *Upstream              `json:"upstream,omitempty"`
+       UpstreamId     string                 `json:"upstream_id,omitempty"`
+       Plugins        map[string]interface{} `json:"plugins"`
+       RouteGroupId   string                 `json:"route_group_id"`
+       RouteGroupName string                 `json:"route_group_name"`
 }
 
 type Route struct {
@@ -413,17 +428,21 @@ type Route struct {
        Content         string `json:"content"`
        Script          string `json:"script"`
        ContentAdminApi string `json:"content_admin_api"`
+       RouteGroupId    string `json:"route_group_id"`
+       RouteGroupName  string `json:"route_group_name"`
 }
 
 type RouteResponse struct {
        Base
-       Name        string    `json:"name"`
-       Description string    `json:"description,omitempty"`
-       Hosts       []string  `json:"hosts,omitempty"`
-       Uris        []string  `json:"uris,omitempty"`
-       Upstream    *Upstream `json:"upstream,omitempty"`
-       UpstreamId  string    `json:"upstream_id,omitempty"`
-       Priority    int64     `json:"priority"`
+       Name           string    `json:"name"`
+       Description    string    `json:"description,omitempty"`
+       Hosts          []string  `json:"hosts,omitempty"`
+       Uris           []string  `json:"uris,omitempty"`
+       Upstream       *Upstream `json:"upstream,omitempty"`
+       UpstreamId     string    `json:"upstream_id,omitempty"`
+       Priority       int64     `json:"priority"`
+       RouteGroupId   string    `json:"route_group_id"`
+       RouteGroupName string    `json:"route_group_name"`
 }
 
 type ListResponse struct {
@@ -437,6 +456,8 @@ func (rr *RouteResponse) Parse(r *Route) {
        rr.Description = r.Description
        rr.UpstreamId = r.UpstreamId
        rr.Priority = r.Priority
+       rr.RouteGroupId = r.RouteGroupId
+       rr.RouteGroupName = r.RouteGroupName
        // hosts
        if len(r.Hosts) > 0 {
                var hosts []string
@@ -575,6 +596,7 @@ func ToRoute(routeRequest *RouteRequest,
        rd.ID = u4
        // content_admin_api
        if resp != nil {
+               resp.Node.Value.RouteGroupId = rd.RouteGroupId
                if respStr, err := json.Marshal(resp); err != nil {
                        e := errno.FromMessage(errno.DBRouteCreateError, 
err.Error())
                        return nil, e
diff --git a/api/service/route_group.go b/api/service/route_group.go
new file mode 100644
index 0000000..b016883
--- /dev/null
+++ b/api/service/route_group.go
@@ -0,0 +1,137 @@
+/*
+ * 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 service
+
+import (
+       "encoding/json"
+       "github.com/apisix/manager-api/conf"
+       "github.com/apisix/manager-api/errno"
+       uuid "github.com/satori/go.uuid"
+)
+
+type RouteGroupDao struct {
+       Base
+       Name        string `json:"name"`
+       Description string `json:"description,omitempty"`
+}
+
+func (RouteGroupDao) TableName() string {
+       return "route_group"
+}
+
+func (rd *RouteGroupDao) CreateRouteGroup() error {
+       return conf.DB().Create(&rd).Error
+}
+
+func (rd *RouteGroupDao) FindRouteGroup(id string) (error, int) {
+       var count int
+       if err := conf.DB().Table("route_group").Where("id=?", 
id).Count(&count).Error; err != nil {
+               return err, 0
+       }
+       conf.DB().Table("route_group").Where("id=?", id).First(&rd)
+       return nil, count
+}
+
+func (rd *RouteGroupDao) GetRouteGroupList(routeGroupList *[]RouteGroupDao, 
search string, page, size int) (error, int) {
+       db := conf.DB()
+       db = db.Table(rd.TableName())
+       if len(search) != 0 {
+               db = db.Where("name like ? or description like ? ", search, 
search)
+       }
+       var count int
+       if err := db.Order("update_time desc").Offset((page - 1) * 
size).Limit(size).Find(&routeGroupList).Error; err != nil {
+               return err, 0
+       } else {
+               if err := db.Count(&count).Error; err != nil {
+                       return err, 0
+               }
+               return nil, count
+       }
+}
+
+func (rd *RouteGroupDao) UpdateRouteGroup() error {
+       db := conf.DB()
+       return db.Model(&RouteGroupDao{}).Update(rd).Error
+}
+
+func (rd *RouteGroupDao) DeleteRouteGroup() error {
+       if err, count := rd.FindRoute(); err != nil {
+               e := errno.FromMessage(errno.RouteGroupSelectRoutesError, 
err.Error())
+               logger.Error(e.Msg)
+               return e
+       } else {
+               if count > 0 {
+                       e := errno.FromMessage(errno.RouteGroupHasRoutesError)
+                       logger.Error(e.Msg)
+                       return e
+               }
+       }
+       return conf.DB().Delete(&rd).Error
+}
+
+type RouteGroupNameResponse struct {
+       ID   string `json:"id"`
+       Name string `json:"name"`
+}
+
+func (u *RouteGroupDao) Parse2NameResponse() (*RouteGroupNameResponse, error) {
+       // routeGroup
+       unr := &RouteGroupNameResponse{
+               ID:   u.ID.String(),
+               Name: u.Name,
+       }
+       return unr, nil
+}
+
+type RouteGroupRequest struct {
+       Id          string `json:"id,omitempty"`
+       Name        string `json:"name"`
+       Description string `json:"description"`
+}
+
+func (u *RouteGroupRequest) toJson() []byte {
+       res, _ := json.Marshal(&u)
+       return res
+}
+
+func (r *RouteGroupRequest) Parse(body interface{}) error {
+       if err := json.Unmarshal(body.([]byte), r); err != nil {
+               r = nil
+               return err
+       }
+       return nil
+}
+
+func Trans2RouteGroupDao(r *RouteGroupRequest) (*RouteGroupDao, 
*errno.ManagerError) {
+
+       u := &RouteGroupDao{
+               Name:        r.Name,
+               Description: r.Description,
+       }
+       // id
+       u.ID = uuid.FromStringOrNil(r.Id)
+       return u, nil
+}
+
+func (r *RouteGroupDao) FindRoute() (error, int) {
+       var count int
+       if err := conf.DB().Table("routes").Where("route_group_id=?", 
r.ID).Count(&count).Error; err != nil {
+               return err, 0
+       }
+       return nil, count
+}

Reply via email to