Jaycean commented on a change in pull request #1245:
URL: https://github.com/apache/apisix-dashboard/pull/1245#discussion_r564543770



##########
File path: api/internal/handler/data_loader/route_export.go
##########
@@ -0,0 +1,478 @@
+/*
+ * 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 data_loader
+
+import (
+       "encoding/json"
+       "fmt"
+       "net/http"
+       "reflect"
+       "strconv"
+       "strings"
+       "time"
+
+       "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/log"
+       "github.com/apisix/manager-api/internal/utils"
+       "github.com/getkin/kin-openapi/openapi3"
+       "github.com/gin-gonic/gin"
+       "github.com/shiningrush/droplet"
+       "github.com/shiningrush/droplet/data"
+       "github.com/shiningrush/droplet/wrapper"
+       wgin "github.com/shiningrush/droplet/wrapper/gin"
+)
+
+type Handler struct {
+       routeStore    store.Interface
+       upstreamStore store.Interface
+       serviceStore  store.Interface
+       consumerStore store.Interface
+}
+
+func NewHandler() (handler.RouteRegister, error) {
+       return &Handler{
+               routeStore:    store.GetStore(store.HubKeyRoute),
+               upstreamStore: store.GetStore(store.HubKeyUpstream),
+               serviceStore:  store.GetStore(store.HubKeyService),
+               consumerStore: store.GetStore(store.HubKeyConsumer),
+       }, nil
+}
+
+func (h *Handler) ApplyRoute(r *gin.Engine) {
+       r.POST("/apisix/admin/routes/export/:ids", wgin.Wraps(h.ExportRoutes,
+               wrapper.InputType(reflect.TypeOf(ExportInput{}))))
+       r.GET("/apisix/admin/exportall/routes", wgin.Wraps(h.ExportAllRoutes))
+}
+
+type ExportInput struct {
+       IDs string `auto_read:"ids,path"`
+}
+
+//ExportRoutes Export data by passing route ID, such as "R1" or multiple route 
parameters, such as "R1,R2"
+func (h *Handler) ExportRoutes(c droplet.Context) (interface{}, error) {
+       input := c.Input().(*ExportInput)
+       ids := strings.Split(input.IDs, ",")
+       routes := []*entity.Route{}
+
+       for _, id := range ids {
+               route, err := h.routeStore.Get(c.Context(), id)
+               if err != nil {
+                       return nil, err
+               }
+               routes = append(routes, route.(*entity.Route))
+       }
+
+       swagger, err := h.RouteToOpenAPI3(c, routes)
+       if err != nil {
+               return nil, err
+       }
+       return swagger, nil
+}
+
+type AuthType string
+
+const (
+       BasicAuth AuthType = "basic-auth"
+       KeyAuth   AuthType = "key-auth"
+       JWTAuth   AuthType = "jwt-auth"
+)
+
+var (
+       openApi = "3.0.0"
+       title   = "RoutesExport"
+       service interface{}
+       err     error
+)
+
+//ExportAllRoutes All routes can be directly exported without passing 
parameters
+func (h *Handler) ExportAllRoutes(c droplet.Context) (interface{}, error) {
+       routelist, err := h.routeStore.List(c.Context(), store.ListInput{})
+
+       if err != nil {
+               return nil, err
+       }
+
+       routes := []*entity.Route{}
+
+       for _, route := range routelist.Rows {
+               routes = append(routes, route.(*entity.Route))
+       }
+
+       swagger, err := h.RouteToOpenAPI3(c, routes)
+       if err != nil {
+               return nil, err
+       }
+       return swagger, nil
+}
+
+//RouteToOpenAPI3 Pass in route list parameter: []*entity.Route, convert route 
data to openapi3 and export processing function
+func (h *Handler) RouteToOpenAPI3(c droplet.Context, routes []*entity.Route) 
(*openapi3.Swagger, error) {
+       paths := openapi3.Paths{}
+       paramsRefs := []*openapi3.ParameterRef{}
+       requestBody := &openapi3.RequestBody{}
+       components := &openapi3.Components{}
+       secSchemas := openapi3.SecuritySchemes{}
+       servicePlugins := make(map[string]interface{})
+       plugins := make(map[string]interface{})
+       serviceLabels := make(map[string]string)
+       labels := make(map[string]string)
+
+       for _, route := range routes {
+               extensions := make(map[string]interface{})
+               pathItem := &openapi3.PathItem{}
+               path := openapi3.Operation{}
+               path.Summary = route.Desc
+               path.OperationID = route.Name
+
+               if route.ServiceID != nil {
+                       serviceID := utils.InterfaceToString(route.ServiceID)
+                       service, err = h.serviceStore.Get(c.Context(), 
serviceID)
+                       if err != nil {
+                               if err == data.ErrNotFound {
+                                       return nil, fmt.Errorf("service id: %s 
not found", route.ServiceID)
+                               }
+                               return nil, err
+                       }
+
+                       _service := service.(*entity.Service)
+                       servicePlugins = _service.Plugins
+                       serviceLabels = _service.Labels
+               }
+
+               //Parse upstream
+               _upstream, err := h.ParseRouteUpstream(c, route)
+
+               if err != nil {
+                       log.Errorf("ParseRouteUpstream err: ", err)
+                       return nil, err
+               } else if err == nil && _upstream != nil {
+                       extensions["x-apisix-upstream"] = _upstream
+               }
+
+               if route.Host != "" {
+                       extensions["x-apisix-host"] = route.Host
+               }
+
+               if route.Hosts != nil {
+                       extensions["x-apisix-hosts"] = route.Hosts
+               }
+
+               //Parse Labels
+               labels, err = ParseLabels(route, serviceLabels, labels)
+               if err != nil {
+                       log.Errorf("parseLabels err: ", err)
+                       return nil, err
+               }
+
+               if labels != nil {
+                       extensions["x-apisix-labels"] = labels
+               }
+
+               if route.RemoteAddr != "" {
+                       extensions["x-apisix-remote_addr"] = route.RemoteAddr
+               }
+
+               if route.RemoteAddrs != nil {
+                       extensions["x-apisix-remote_addrs"] = route.RemoteAddrs
+               }
+
+               if route.FilterFunc != "" {
+                       extensions["x-apisix-filter_func"] = route.FilterFunc
+               }
+
+               if route.Script != nil {
+                       extensions["x-apisix-script"] = route.Script
+               }
+
+               if route.ServiceProtocol != "" {
+                       extensions["x-apisix-service_protocol"] = 
route.ServiceProtocol
+               }
+
+               if route.Vars != nil {
+                       extensions["x-apisix-vars"] = route.Vars
+               }
+
+               // Parse Route URIs
+               paths, paramsRefs = ParseRouteUris(route, paths, paramsRefs, 
pathItem)
+
+               //Parse Route Plugins
+               path, secSchemas, paramsRefs, plugins, err = 
ParseRoutePlugins(route, paramsRefs, plugins, path, servicePlugins, secSchemas, 
requestBody)
+
+               if err != nil {
+                       log.Errorf("parseRoutePlugins err: ", err)
+                       return nil, err
+               }
+
+               if plugins != nil {
+                       extensions["x-apisix-plugins"] = plugins
+               }
+
+               extensions["x-apisix-priority"] = route.Priority
+               extensions["x-apisix-status"] = route.Status
+               extensions["x-apisix-enable_websocket"] = route.EnableWebsocket
+               path.Extensions = extensions
+               path.Parameters = paramsRefs
+               path.RequestBody = &openapi3.RequestBodyRef{Value: requestBody}
+               path.Responses = openapi3.NewResponses()
+
+               for i := range route.Methods {
+                       switch strings.ToUpper(route.Methods[i]) {
+                       case http.MethodGet:
+                               pathItem.Get = ParsePathItem(path, 
http.MethodGet)
+                       case http.MethodPost:
+                               pathItem.Post = ParsePathItem(path, 
http.MethodPost)
+                       case http.MethodPut:
+                               pathItem.Put = ParsePathItem(path, 
http.MethodPut)
+                       case http.MethodDelete:
+                               pathItem.Delete = ParsePathItem(path, 
http.MethodDelete)
+                       case http.MethodPatch:
+                               pathItem.Patch = ParsePathItem(path, 
http.MethodPatch)
+                       case http.MethodHead:
+                               pathItem.Head = ParsePathItem(path, 
http.MethodHead)
+                       }
+               }
+       }
+
+       components.SecuritySchemes = secSchemas
+       swagger := openapi3.Swagger{
+               OpenAPI:    openApi,
+               Info:       &openapi3.Info{Title: title, Version: openApi},
+               Paths:      paths,
+               Components: *components,
+       }
+       return &swagger, nil
+}
+
+//ParseLabels When service and route have labels at the same time, use route's 
label.
+//When route has no label, service sometimes uses service's label. This 
function is used to process this logic
+func ParseLabels(route *entity.Route, serviceLabels map[string]string, labels 
map[string]string) (map[string]string, error) {
+       if route.Labels != nil {
+               return route.Labels, nil
+       } else if route.Labels == nil && route.ServiceID != nil {
+               if serviceLabels != nil {
+                       return serviceLabels, nil
+               } else if serviceLabels == nil {
+                       return nil, nil
+               }
+       }
+       return nil, nil
+}
+
+//ParsePathItem Convert data in route to openapi3
+func ParsePathItem(path openapi3.Operation, routeMethod string) 
*openapi3.Operation {
+       _path := &openapi3.Operation{
+               ExtensionProps: path.ExtensionProps,
+               Tags:           path.Tags,
+               Summary:        path.Summary,
+               Description:    path.Description,
+               OperationID:    path.OperationID + routeMethod,
+               Parameters:     path.Parameters,
+               RequestBody:    path.RequestBody,
+               Responses:      path.Responses,
+               Callbacks:      path.Callbacks,
+               Deprecated:     path.Deprecated,
+               Security:       path.Security,
+               Servers:        path.Servers,
+               ExternalDocs:   path.ExternalDocs,
+       }
+       return _path
+}
+
+// ParseRoutePlugins Merge service with plugin in route
+func ParseRoutePlugins(route *entity.Route, paramsRefs 
[]*openapi3.ParameterRef, plugins map[string]interface{}, path 
openapi3.Operation, servicePlugins map[string]interface{}, secSchemas 
openapi3.SecuritySchemes, requestBody *openapi3.RequestBody) 
(openapi3.Operation, openapi3.SecuritySchemes, []*openapi3.ParameterRef, 
map[string]interface{}, error) {
+       if route.Plugins != nil {
+               param := &openapi3.Parameter{}
+               secReq := &openapi3.SecurityRequirements{}
+
+               // analysis plugins
+               for key, value := range route.Plugins {
+                       // analysis proxy-rewrite plugin
+                       //if key == "proxy-rewrite" {
+                       //      continue
+                       //}
+
+                       // analysis request-validation plugin
+                       if key == "request-validation" {
+                               if valueMap, ok := 
value.(map[string]interface{}); ok {
+                                       if hsVal, ok := 
valueMap["header_schema"]; ok {
+                                               param.In = "header"
+                                               requestValidation := 
&entity.RequestValidation{}
+                                               reqBytes, _ := 
json.Marshal(&hsVal)
+                                               err := json.Unmarshal(reqBytes, 
requestValidation)
+                                               if err != nil {
+                                                       log.Errorf("json 
marshal failed: %s", err)
+                                               }
+                                               for key1, value1 := range 
requestValidation.Properties.(map[string]interface{}) {
+                                                       for _, arr := range 
requestValidation.Required {
+                                                               if arr == key1 {
+                                                                       
param.Required = true
+                                                               }
+                                                       }
+                                                       param.Name = key1
+                                                       typeStr := 
value1.(map[string]interface{})
+                                                       schema := 
&openapi3.Schema{Type: typeStr["type"].(string)}
+                                                       param.Schema = 
&openapi3.SchemaRef{Value: schema}
+                                                       paramsRefs = 
append(paramsRefs, &openapi3.ParameterRef{Value: param})
+                                               }
+                                       }
+
+                                       if bsVal, ok := 
valueMap["body_schema"]; ok {
+                                               m := 
map[string]*openapi3.MediaType{}
+                                               reqBytes, _ := 
json.Marshal(&bsVal)
+                                               schema := &openapi3.Schema{}
+                                               err := json.Unmarshal(reqBytes, 
schema)
+                                               if err != nil {
+                                                       log.Errorf("json 
marshal failed: %s", err)
+                                               }
+
+                                               m["*/*"] = 
&openapi3.MediaType{Schema: &openapi3.SchemaRef{Value: schema}}
+                                               requestBody.Content = m
+                                       }
+                               }
+                               continue
+                       }
+                       // analysis security plugins
+                       securityEnv := &openapi3.SecurityRequirement{}
+                       switch key {
+                       case string(KeyAuth):
+                               secSchemas["api_key"] = 
&openapi3.SecuritySchemeRef{Value: openapi3.NewCSRFSecurityScheme()}
+                               securityEnv.Authenticate("api_key", " ")
+                               secReq.With(*securityEnv)
+                               continue
+                       case string(BasicAuth):
+                               secSchemas["basicAuth"] = 
&openapi3.SecuritySchemeRef{Value: &openapi3.SecurityScheme{
+                                       Type: "basicAuth",
+                                       Name: "basicAuth",
+                                       In:   "header",
+                               }}
+                               securityEnv.Authenticate("basicAuth", " ")
+                               secReq.With(*securityEnv)
+                               continue
+                       case string(JWTAuth):
+                               secSchemas["bearerAuth"] = 
&openapi3.SecuritySchemeRef{Value: openapi3.NewJWTSecurityScheme()}
+                               securityEnv.Authenticate("bearerAuth", " ")
+                               secReq.With(*securityEnv)
+                               continue
+                       }
+                       plugins[key] = value
+               }
+               path.Security = secReq
+
+               if route.ServiceID != nil && servicePlugins != nil {
+                       _servicePlugins, err := json.Marshal(servicePlugins)
+                       if err != nil {
+                               log.Errorf("MapToJson err: ", err)
+                               return path, nil, nil, nil, err
+                       }
+                       _plugins, err := json.Marshal(plugins)
+                       if err != nil {
+                               log.Errorf("MapToJson err: ", err)
+                               return path, nil, nil, nil, err
+                       }
+                       bytePlugins, err := utils.MergeJson(_servicePlugins, 
_plugins)
+                       if err != nil {
+                               log.Errorf("Plugins MergeJson err: ", err)
+                               return path, nil, nil, nil, err
+                       }
+                       err = json.Unmarshal([]byte(bytePlugins), &plugins)
+                       if err != nil {
+                               log.Errorf("JsonToMapDemo err: ", err)
+                               return path, nil, nil, nil, err
+                       }
+               }
+       } else if route.Plugins == nil && route.ServiceID != nil {
+               plugins = servicePlugins
+       }
+       return path, secSchemas, paramsRefs, plugins, nil
+}
+
+// ParseRouteUris The URI and URIs of route are converted to paths URI in 
openapi3
+func ParseRouteUris(route *entity.Route, paths openapi3.Paths, paramsRefs 
[]*openapi3.ParameterRef, pathItem *openapi3.PathItem) (openapi3.Paths, 
[]*openapi3.ParameterRef) {
+       routeURIs := []string{}
+       if route.URI != "" {
+               routeURIs = append(routeURIs, route.URI)
+       }
+
+       if route.Uris != nil {
+               routeURIs = route.Uris
+       }
+
+       for _, uri := range routeURIs {
+               _time := time.Now().UnixNano() / 1e6
+               if strings.Contains(uri, "*") {
+                       if _, ok := paths[strings.Split(uri, 
"*")[0]+"{params}"]; !ok {
+                               paths[strings.Split(uri, "*")[0]+"{params}"] = 
pathItem
+                       } else {
+                               paths[strings.Split(uri, 
"*")[0]+"{params}"+"-APISIX-REPEAT-URI-"+strconv.FormatInt(_time, 10)] = 
pathItem
+                       }

Review comment:
       Here I add a global variable, which is initialized to 0 every time I 
call the interface. In the loop, if it meets the conditions, it will 
automatically increase to 1. I don't know if this is OK

##########
File path: api/internal/handler/data_loader/route_export_test.go
##########
@@ -0,0 +1,1864 @@
+/*
+ * 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 data_loader
+
+import (
+       "encoding/json"
+       "strings"
+       "testing"
+
+       "github.com/apisix/manager-api/internal/core/entity"
+       "github.com/apisix/manager-api/internal/core/store"
+       "github.com/shiningrush/droplet"
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/mock"
+)
+
+// 1.Export data as the route of URIs Hosts
+func TestExportRoutes1(t *testing.T) {
+       input := &ExportInput{IDs: "1"}
+       //*entity.Route
+       r1 := `{
+               "name": "aaaa",
+               "labels": {
+                       "build":"16",
+                       "env":"production",
+                       "version":"v2"
+               },
+               "plugins": {
+                       "limit-count": {
+                               "count": 2,
+                               "time_window": 60,
+                               "rejected_code": 503,
+                               "key": "remote_addr"
+                       }
+               },
+               "status": 1,
+               "uris": ["/hello_"],
+               "hosts": ["foo.com", "*.bar.com"],
+               "methods": ["GET", "POST"],
+               "upstream": {
+                       "nodes": {
+                               "172.16.238.20:1980": 1
+                       },
+                       "type": "roundrobin"
+               }
+       }`
+
+       exportR1 := `{
+               "components": {},
+               "info": {
+                       "title": "RoutesExport",
+                       "version": "3.0.0"
+               },
+               "openapi": "3.0.0",
+               "paths": {
+                       "/hello_": {
+                               "get": {
+                                       "operationId": "aaaaGET",
+                                       "requestBody": {},
+                                       "responses": {
+                                               "default": {
+                                                       "description": ""
+                                               }
+                                       },
+                                       "security": [],
+                                       "x-apisix-enable_websocket": false,
+                                       "x-apisix-hosts": ["foo.com", 
"*.bar.com"],
+                                       "x-apisix-labels": {
+                                               "build": "16",
+                                               "env": "production",
+                                               "version": "v2"
+                                       },
+                                       "x-apisix-plugins": {
+                                               "limit-count": {
+                                                       "count": 2,
+                                                       "key": "remote_addr",
+                                                       "rejected_code": 503,
+                                                       "time_window": 60
+                                               }
+                                       },
+                                       "x-apisix-priority": 0,
+                                       "x-apisix-status": 1,
+                                       "x-apisix-upstream": {
+                                               "nodes": {
+                                                       "172.16.238.20:1980": 1
+                                               },
+                                               "type": "roundrobin"
+                                       }
+                               },
+                               "post": {
+                                       "operationId": "aaaaPOST",
+                                       "requestBody": {},
+                                       "responses": {
+                                               "default": {
+                                                       "description": ""
+                                               }
+                                       },
+                                       "security": [],
+                                       "x-apisix-enable_websocket": false,
+                                       "x-apisix-hosts": ["foo.com", 
"*.bar.com"],
+                                       "x-apisix-labels": {
+                                               "build": "16",
+                                               "env": "production",
+                                               "version": "v2"
+                                       },
+                                       "x-apisix-plugins": {
+                                               "limit-count": {
+                                                       "count": 2,
+                                                       "key": "remote_addr",
+                                                       "rejected_code": 503,
+                                                       "time_window": 60
+                                               }
+                                       },
+                                       "x-apisix-priority": 0,
+                                       "x-apisix-status": 1,
+                                       "x-apisix-upstream": {
+                                               "nodes": {
+                                                       "172.16.238.20:1980": 1
+                                               },
+                                               "type": "roundrobin"
+                                       }
+                               }
+                       }
+               }
+       }`
+       var route *entity.Route
+       err := json.Unmarshal([]byte(r1), &route)
+       mStore := &store.MockInterface{}
+       mStore.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(route, nil)
+
+       h := Handler{routeStore: mStore}
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       ret, err := h.ExportRoutes(ctx)
+       assert.Nil(t, err)
+       ret1, err := json.Marshal(ret)
+       if err != nil {
+       }
+       assert.Equal(t, replaceStr(exportR1), string(ret1))
+       assert.NotNil(t, ret1)
+}
+
+// 2.Export data as the route of URI host
+func TestExportRoutes2(t *testing.T) {
+       input := &ExportInput{IDs: "1"}
+       //*entity.Route
+       r2 := `{
+               "name": "aaaa2",
+               "labels": {
+                       "build":"16",
+                       "env":"production",
+                       "version":"v2"
+               },
+               "plugins": {
+                       "limit-count": {
+                               "count": 2,
+                               "time_window": 60,
+                               "rejected_code": 503,
+                               "key": "remote_addr"
+                       }
+               },
+               "status": 1,
+               "uri": "/hello2",
+               "host": "*.bar.com",
+               "methods": ["GET", "POST"],
+               "upstream": {
+                       "nodes": {
+                               "172.16.238.20:1980": 1
+                       },
+                       "type": "roundrobin"
+               }
+       }`
+
+       exportR2 := `{
+               "components": {},
+               "info": {
+                       "title": "RoutesExport",
+                       "version": "3.0.0"
+               },
+               "openapi": "3.0.0",
+               "paths": {
+                       "/hello2": {
+                               "get": {
+                                       "operationId": "aaaa2GET",
+                                       "requestBody": {},
+                                       "responses": {
+                                               "default": {
+                                                       "description": ""
+                                               }
+                                       },
+                                       "security": [],
+                                       "x-apisix-enable_websocket": false,
+                                       "x-apisix-host": "*.bar.com",
+                                       "x-apisix-labels": {
+                                               "build": "16",
+                                               "env": "production",
+                                               "version": "v2"
+                                       },
+                                       "x-apisix-plugins": {
+                                               "limit-count": {
+                                                       "count": 2,
+                                                       "key": "remote_addr",
+                                                       "rejected_code": 503,
+                                                       "time_window": 60
+                                               }
+                                       },
+                                       "x-apisix-priority": 0,
+                                       "x-apisix-status": 1,
+                                       "x-apisix-upstream": {
+                                               "nodes": {
+                                                       "172.16.238.20:1980": 1
+                                               },
+                                               "type": "roundrobin"
+                                       }
+                               },
+                               "post": {
+                                       "operationId": "aaaa2POST",
+                                       "requestBody": {},
+                                       "responses": {
+                                               "default": {
+                                                       "description": ""
+                                               }
+                                       },
+                                       "security": [],
+                                       "x-apisix-enable_websocket": false,
+                                       "x-apisix-host": "*.bar.com",
+                                       "x-apisix-labels": {
+                                               "build": "16",
+                                               "env": "production",
+                                               "version": "v2"
+                                       },
+                                       "x-apisix-plugins": {
+                                               "limit-count": {
+                                                       "count": 2,
+                                                       "key": "remote_addr",
+                                                       "rejected_code": 503,
+                                                       "time_window": 60
+                                               }
+                                       },
+                                       "x-apisix-priority": 0,
+                                       "x-apisix-status": 1,
+                                       "x-apisix-upstream": {
+                                               "nodes": {
+                                                       "172.16.238.20:1980": 1
+                                               },
+                                               "type": "roundrobin"
+                                       }
+                               }
+                       }
+               }
+       }`
+       var route *entity.Route
+       err := json.Unmarshal([]byte(r2), &route)
+       mStore := &store.MockInterface{}
+       mStore.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(route, nil)
+
+       h := Handler{routeStore: mStore}
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       ret, err := h.ExportRoutes(ctx)
+       assert.Nil(t, err)
+       ret1, err := json.Marshal(ret)
+       if err != nil {
+       }
+       assert.Equal(t, replaceStr(exportR2), string(ret1))
+       assert.NotNil(t, ret1)
+}
+
+// 3.Create a service that contains complete data and use the service_id 
create route
+func TestExportRoutesCreateByServiceId(t *testing.T) {
+       input := &ExportInput{IDs: "1"}
+       //*entity.Route
+       r := `{
+                               "methods": ["GET"],
+                               "uri": "/hello",
+                               "service_id": "s1"
+                       }`
+
+       s := `{
+               "id": "s1",
+               "name": "testservice",
+               "desc": "testservice_desc",
+               "labels": {
+                       "build":"16",
+                       "env":"production",
+                       "version":"v2"
+               },
+               "enable_websocket":true,
+               "plugins": { 
+                       "limit-count": { 
+                               "count": 100, 
+                               "time_window": 60, 
+                               "rejected_code": 503, 
+                               "key": "remote_addr" 
+                       } 
+               },
+               "upstream": {
+                       "type": "roundrobin",
+                       "nodes": [{
+                               "host": "172.16.238.20",
+                               "port": 1980,
+                               "weight": 1
+                       }]
+               }
+       }`
+
+       exportR := `{
+               "components": {},
+               "info": {
+                       "title": "RoutesExport",
+                       "version": "3.0.0"
+               },
+               "openapi": "3.0.0",
+               "paths": {
+                       "/hello": {
+                               "get": {
+                                       "operationId": "GET",
+                                       "requestBody": {},
+                                       "responses": {
+                                               "default": {
+                                                       "description": ""
+                                               }
+                                       },
+                                       "x-apisix-enable_websocket": false,
+                                       "x-apisix-labels": {
+                                               "build": "16",
+                                               "env": "production",
+                                               "version": "v2"
+                                       },
+                                       "x-apisix-plugins": {
+                                               "limit-count": {
+                                                       "count": 100,
+                                                       "key": "remote_addr",
+                                                       "rejected_code": 503,
+                                                       "time_window": 60
+                                               }
+                                       },
+                                       "x-apisix-priority": 0,
+                                       "x-apisix-status": 0,
+                                       "x-apisix-upstream": {
+                                               "nodes": [{
+                                                       "host": "172.16.238.20",
+                                                       "port": 1980,
+                                                       "weight": 1
+                                               }],
+                                               "type": "roundrobin"
+                                       }
+                               }
+                       }
+               }
+       }`
+       var route *entity.Route
+       var service *entity.Service
+       err := json.Unmarshal([]byte(r), &route)
+       err = json.Unmarshal([]byte(s), &service)
+       mStore := &store.MockInterface{}
+       mStore.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(route, nil)
+       mStoreService := &store.MockInterface{}
+       mStoreService.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(service, nil)
+
+       h := Handler{routeStore: mStore, serviceStore: mStoreService}
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       ret, err := h.ExportRoutes(ctx)
+       assert.Nil(t, err)
+       _ret, err := json.Marshal(ret)
+       if err != nil {
+       }
+       assert.Equal(t, replaceStr(exportR), string(_ret))
+       assert.NotNil(t, _ret)
+}
+
+// 4.Create a service containing plugin and a route containing plugin to test 
the fusion of exported data
+func TestExportRoutesCreateByServiceId2(t *testing.T) {
+       input := &ExportInput{IDs: "1"}
+       //*entity.Route
+       r := `{
+               "methods": ["GET"],
+               "uri": "/hello",
+               "service_id": "s1",
+               "enable_websocket":false,
+               "plugins": { 
+                       "prometheus": {
+                               "disable": false
+                       }
+               },
+               "upstream": {
+                       "type": "roundrobin",
+                       "nodes": [{
+                               "host": "172.16.238.20",
+                               "port": 1980,
+                               "weight": 1
+                       }]
+               }
+       }`
+
+       s := `{
+               "id": "s1",
+               "name": "testservice",
+               "desc": "testservice_desc",
+               "labels": {
+                       "build":"16",
+                       "env":"production",
+                       "version":"v2"
+               },
+               "enable_websocket":true,
+               "plugins": { 
+                       "limit-count": { 
+                               "count": 100, 
+                               "time_window": 60, 
+                               "rejected_code": 503, 
+                               "key": "remote_addr" 
+                       } 
+               },
+               "upstream": {
+                       "type": "roundrobin",
+                       "nodes": [{
+                               "host": "172.16.238.20",
+                               "port": 1980,
+                               "weight": 1
+                       }]
+               }
+       }`
+
+       exportR := `{
+               "components": {},
+               "info": {
+                       "title": "RoutesExport",
+                       "version": "3.0.0"
+               },
+               "openapi": "3.0.0",
+               "paths": {
+                       "/hello": {
+                               "get": {
+                                       "operationId": "GET",
+                                       "requestBody": {},
+                                       "responses": {
+                                               "default": {
+                                                       "description": ""
+                                               }
+                                       },
+                                       "security": [],
+                                       "x-apisix-enable_websocket": false,
+                                       "x-apisix-labels": {
+                                               "build": "16",
+                                               "env": "production",
+                                               "version": "v2"
+                                       },
+                                       "x-apisix-plugins": {
+                                               "limit-count": {
+                                                       "count": 100,
+                                                       "key": "remote_addr",
+                                                       "rejected_code": 503,
+                                                       "time_window": 60
+                                               },
+                                               "prometheus": {
+                                                       "disable": false
+                                               }
+                                       },
+                                       "x-apisix-priority": 0,
+                                       "x-apisix-status": 0,
+                                       "x-apisix-upstream": {
+                                               "nodes": [{
+                                                       "host": "172.16.238.20",
+                                                       "port": 1980,
+                                                       "weight": 1
+                                               }],
+                                               "type": "roundrobin"
+                                       }
+                               }
+                       }
+               }
+       }`
+       var route *entity.Route
+       var service *entity.Service
+       err := json.Unmarshal([]byte(r), &route)
+       err = json.Unmarshal([]byte(s), &service)
+       mStore := &store.MockInterface{}
+       mStore.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(route, nil)
+       mStoreService := &store.MockInterface{}
+       mStoreService.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(service, nil)
+
+       h := Handler{routeStore: mStore, serviceStore: mStoreService}
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       ret, err := h.ExportRoutes(ctx)
+       assert.Nil(t, err)
+       _ret, err := json.Marshal(ret)
+       if err != nil {
+       }
+       assert.Equal(t, replaceStr(exportR), string(_ret))
+       assert.NotNil(t, _ret)
+}
+
+// 5.Create a service according to the upstream ID and a route according to 
the service ID
+func TestExportRoutesCreateByServiceId3(t *testing.T) {
+       input := &ExportInput{IDs: "1"}
+       us := `{
+               "id": "u1",
+               "nodes": [
+                       {
+                               "host": "172.16.238.20",
+                               "port": 1980,
+                               "weight": 1
+                       }
+               ],
+               "type": "roundrobin"
+       }`
+
+       s := `{
+               "id": "s1",
+               "name": "testservice",
+               "desc": "testservice_desc",
+               "labels": {
+                       "build":"16",
+                       "env":"production",
+                       "version":"v2"
+               },
+               "enable_websocket":true,
+               "plugins": { 
+                       "limit-count": { 
+                               "count": 100, 
+                               "time_window": 60, 
+                               "rejected_code": 503, 
+                               "key": "remote_addr" 
+                       } 
+               },
+               "upstream_id": "u1"
+       }`
+
+       r := `{
+               "methods": ["GET"],
+               "uri": "/hello",
+               "service_id": "s1",
+               "enable_websocket":false,
+               "plugins": { 
+                       "prometheus": {
+                               "disable": false
+                       }
+               },
+               "upstream": {
+                       "type": "roundrobin",
+                       "nodes": [{
+                               "host": "172.16.238.20",
+                               "port": 1981,
+                               "weight": 1
+                       }]
+               }
+       }`
+
+       exportR := `{
+               "components": {},
+               "info": {
+                       "title": "RoutesExport",
+                       "version": "3.0.0"
+               },
+               "openapi": "3.0.0",
+               "paths": {
+                       "/hello": {
+                               "get": {
+                                       "operationId": "GET",
+                                       "requestBody": {},
+                                       "responses": {
+                                               "default": {
+                                                       "description": ""
+                                               }
+                                       },
+                                       "security": [],
+                                       "x-apisix-enable_websocket": false,
+                                       "x-apisix-labels": {
+                                               "build": "16",
+                                               "env": "production",
+                                               "version": "v2"
+                                       },
+                                       "x-apisix-plugins": {
+                                               "limit-count": {
+                                                       "count": 100,
+                                                       "key": "remote_addr",
+                                                       "rejected_code": 503,
+                                                       "time_window": 60
+                                               },
+                                               "prometheus": {
+                                                       "disable": false
+                                               }
+                                       },
+                                       "x-apisix-priority": 0,
+                                       "x-apisix-status": 0,
+                                       "x-apisix-upstream": {
+                                               "nodes": [{
+                                                       "host": "172.16.238.20",
+                                                       "port": 1981,
+                                                       "weight": 1
+                                               }],
+                                               "type": "roundrobin"
+                                       }
+                               }
+                       }
+               }
+       }`
+       var route *entity.Route
+       var service *entity.Service
+       var upstream *entity.Upstream
+       err := json.Unmarshal([]byte(r), &route)
+       err = json.Unmarshal([]byte(s), &service)
+       err = json.Unmarshal([]byte(us), &upstream)
+
+       mStore := &store.MockInterface{}
+       mStore.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(route, nil)
+
+       mStoreService := &store.MockInterface{}
+       mStoreService.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(service, nil)
+
+       mStoreUpstream := &store.MockInterface{}
+       mStoreUpstream.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(upstream, nil)
+
+       h := Handler{routeStore: mStore, serviceStore: mStoreService, 
upstreamStore: mStoreUpstream}
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       ret, err := h.ExportRoutes(ctx)
+       assert.Nil(t, err)
+       _ret, err := json.Marshal(ret)
+       if err != nil {
+       }
+       assert.Equal(t, replaceStr(exportR), string(_ret))
+       assert.NotNil(t, _ret)
+}
+
+// 6.Create and export route according to upstream ID
+func TestExportRoutesCreateByUpstreamId(t *testing.T) {
+       input := &ExportInput{IDs: "1"}
+       us := `{
+               "id": "u1",
+               "nodes": [
+                       {
+                               "host": "172.16.238.20",
+                               "port": 1980,
+                               "weight": 1
+                       }
+               ],
+               "type": "roundrobin"
+       }`
+
+       r := `{
+               "methods": ["GET"],
+               "uri": "/hello",
+               "enable_websocket":false,
+               "plugins": { 
+                       "prometheus": {
+                               "disable": false
+                       }
+               },
+               "upstream_id": "u1"
+       }`
+
+       exportR := `{
+               "components": {},
+               "info": {
+                       "title": "RoutesExport",
+                       "version": "3.0.0"
+               },
+               "openapi": "3.0.0",
+               "paths": {
+                       "/hello": {
+                               "get": {
+                                       "operationId": "GET",
+                                       "requestBody": {},
+                                       "responses": {
+                                               "default": {
+                                                       "description": ""
+                                               }
+                                       },
+                                       "security": [],
+                                       "x-apisix-enable_websocket": false,
+                                       "x-apisix-plugins": {
+                                               "prometheus": {
+                                                       "disable": false
+                                               }
+                                       },
+                                       "x-apisix-priority": 0,
+                                       "x-apisix-status": 0,
+                                       "x-apisix-upstream": {
+                                               "id": "u1",
+                                               "nodes": [{
+                                                       "host": "172.16.238.20",
+                                                       "port": 1980,
+                                                       "weight": 1
+                                               }],
+                                               "type": "roundrobin"
+                                       }
+                               }
+                       }
+               }
+       }`
+       var route *entity.Route
+       var upstream *entity.Upstream
+       err := json.Unmarshal([]byte(r), &route)
+       err = json.Unmarshal([]byte(us), &upstream)
+
+       mStore := &store.MockInterface{}
+       mStore.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(route, nil)
+
+       mStoreUpstream := &store.MockInterface{}
+       mStoreUpstream.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(upstream, nil)
+
+       h := Handler{routeStore: mStore, upstreamStore: mStoreUpstream}
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       ret, err := h.ExportRoutes(ctx)
+       assert.Nil(t, err)
+       _ret, err := json.Marshal(ret)
+       if err != nil {
+       }
+       assert.Equal(t, replaceStr(exportR), string(_ret))
+       assert.NotNil(t, _ret)
+}
+
+// 7.Create route according to upstream ID and service ID
+func TestExportRoutesCreateByUpstreamIdandServiceId(t *testing.T) {
+       input := &ExportInput{IDs: "1"}
+       us := `{
+               "id": "u1",
+               "nodes": [
+                       {
+                               "host": "172.16.238.20",
+                               "port": 1980,
+                               "weight": 1
+                       }
+               ],
+               "type": "roundrobin"
+       }`
+
+       s := `{
+               "id": "s1",
+               "name": "testservice",
+               "desc": "testservice_desc",
+               "labels": {
+                       "build":"16",
+                       "env":"production",
+                       "version":"v2"
+               },
+               "enable_websocket":true,
+               "plugins": { 
+                       "limit-count": { 
+                               "count": 100, 
+                               "time_window": 60, 
+                               "rejected_code": 503, 
+                               "key": "remote_addr" 
+                       } 
+               },
+               "upstream_id": "u1"
+       }`
+
+       r := `{
+               "name": "route_all",
+               "desc": "所有",
+               "status": 1,
+               "methods": ["GET"],
+               "priority": 0,
+               "service_id": "s1",
+               "labels": {
+                       "test": "1",
+                       "API_VERSION": "v1"
+               },
+               "vars": [
+                       ["arg_name", "==", "test"]
+               ],
+               "uri": "/hello",
+               "enable_websocket":false,
+               "plugins": { 
+                       "prometheus": {
+                               "disable": false
+                       }
+               },
+               "upstream_id": "u1"
+       }`
+
+       exportR := `{
+               "components": {},
+               "info": {
+                       "title": "RoutesExport",
+                       "version": "3.0.0"
+               },
+               "openapi": "3.0.0",
+               "paths": {
+                       "/hello": {
+                               "get": {
+                                       "operationId": "route_allGET",
+                                       "requestBody": {},
+                                       "responses": {
+                                               "default": {
+                                                       "description": ""
+                                               }
+                                       },
+                                       "security": [],
+                                       "summary": "所有",
+                                       "x-apisix-enable_websocket": false,
+                                       "x-apisix-labels": {
+                                               "API_VERSION": "v1",
+                                               "test": "1"
+                                       },
+                                       "x-apisix-plugins": {
+                                               "limit-count": {
+                                                       "count": 100,
+                                                       "key": "remote_addr",
+                                                       "rejected_code": 503,
+                                                       "time_window": 60
+                                               },
+                                               "prometheus": {
+                                                       "disable": false
+                                               }
+                                       },
+                                       "x-apisix-priority": 0,
+                                       "x-apisix-status": 1,
+                                       "x-apisix-upstream": {
+                                               "id": "u1",
+                                               "nodes": [{
+                                                       "host": "172.16.238.20",
+                                                       "port": 1980,
+                                                       "weight": 1
+                                               }],
+                                               "type": "roundrobin"
+                                       },
+                                       "x-apisix-vars": [
+                                               ["arg_name", "==", "test"]
+                                       ]
+                               }
+                       }
+               }
+       }`
+       var route *entity.Route
+       var service *entity.Service
+       var upstream *entity.Upstream
+       err := json.Unmarshal([]byte(r), &route)
+       err = json.Unmarshal([]byte(s), &service)
+       err = json.Unmarshal([]byte(us), &upstream)
+
+       mStore := &store.MockInterface{}
+       mStore.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(route, nil)
+
+       mStoreUpstream := &store.MockInterface{}
+       mStoreUpstream.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(upstream, nil)
+
+       mStoreService := &store.MockInterface{}
+       mStoreService.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(service, nil)
+
+       h := Handler{routeStore: mStore, upstreamStore: mStoreUpstream, 
serviceStore: mStoreService}
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       ret, err := h.ExportRoutes(ctx)
+       assert.Nil(t, err)
+       _ret, err := json.Marshal(ret)
+       if err != nil {
+       }
+       assert.Equal(t, replaceStr(exportR), string(_ret))
+       assert.NotNil(t, _ret)
+}
+
+// 8.Creating route using service ID does not contain upstream data
+func TestExportRoutesCreateByServiceIdNoUpstream(t *testing.T) {
+       input := &ExportInput{IDs: "1"}
+       us := `{
+               "id": "u1",
+               "nodes": [
+                       {
+                               "host": "172.16.238.20",
+                               "port": 1980,
+                               "weight": 1
+                       }
+               ],
+               "type": "roundrobin"
+       }`
+
+       s := `{
+               "name": "testservice",
+               "desc": "testservice_desc",
+               "enable_websocket":true,
+               "upstream_id": "6"
+       }`
+
+       r := `{
+               "name": "route_all",
+               "desc": "所有",
+               "status": 1,
+               "methods": ["GET"],
+               "priority": 0,
+               "service_id": "s5",
+               "labels": {
+                       "test": "1",
+                       "API_VERSION": "v1"
+               },
+               "vars": [
+                       ["arg_name", "==", "test"]
+               ],
+               "uri": "/hello",
+               "enable_websocket":false,
+               "plugins": { 
+                       "prometheus": {
+                               "disable": false
+                       }
+               }
+       }`
+
+       exportR := `{
+               "components": {},
+               "info": {
+                       "title": "RoutesExport",
+                       "version": "3.0.0"
+               },
+               "openapi": "3.0.0",
+               "paths": {
+                       "/hello": {
+                               "get": {
+                                       "operationId": "route_allGET",
+                                       "requestBody": {},
+                                       "responses": {
+                                               "default": {
+                                                       "description": ""
+                                               }
+                                       },
+                                       "security": [],
+                                       "summary": "所有",
+                                       "x-apisix-enable_websocket": false,
+                                       "x-apisix-labels": {
+                                               "API_VERSION": "v1",
+                                               "test": "1"
+                                       },
+                                       "x-apisix-plugins": {
+                                               "prometheus": {
+                                                       "disable": false
+                                               }
+                                       },
+                                       "x-apisix-priority": 0,
+                                       "x-apisix-status": 1,
+                                       "x-apisix-upstream": {
+                                               "id": "u1",
+                                               "nodes": [{
+                                                       "host": "172.16.238.20",
+                                                       "port": 1980,
+                                                       "weight": 1
+                                               }],
+                                               "type": "roundrobin"
+                                       },
+                                       "x-apisix-vars": [
+                                               ["arg_name", "==", "test"]
+                                       ]
+                               }
+                       }
+               }
+       }`
+       var route *entity.Route
+       var service *entity.Service
+       var upstream *entity.Upstream
+       err := json.Unmarshal([]byte(r), &route)
+       err = json.Unmarshal([]byte(s), &service)
+       err = json.Unmarshal([]byte(us), &upstream)
+
+       mStore := &store.MockInterface{}
+       mStore.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(route, nil)
+
+       mStoreUpstream := &store.MockInterface{}
+       mStoreUpstream.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(upstream, nil)
+
+       mStoreService := &store.MockInterface{}
+       mStoreService.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(service, nil)
+
+       h := Handler{routeStore: mStore, upstreamStore: mStoreUpstream, 
serviceStore: mStoreService}
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       ret, err := h.ExportRoutes(ctx)
+       assert.Nil(t, err)
+       _ret, err := json.Marshal(ret)
+       if err != nil {
+       }
+       assert.Equal(t, replaceStr(exportR), string(_ret))
+       assert.NotNil(t, _ret)
+}
+
+// 9.Create a service with label data and a route with label data, and export 
the route.
+// Label is the original data of the route
+func TestExportRoutesCreateByLabel(t *testing.T) {
+       input := &ExportInput{IDs: "1"}
+       s := `{
+               "name": "testservice",
+               "desc": "testservice_desc",
+               "enable_websocket":true,
+               "labels": {
+                       "build": "10"
+               }
+       }`
+
+       r := `{
+               "name": "route_all",
+               "desc": "所有",
+               "status": 1,
+               "methods": ["GET"],
+               "service_id": "s1",
+               "labels": {
+                       "test": "1",
+                       "API_VERSION": "v1"
+               },
+               "uri": "/hello",
+               "enable_websocket":false,
+               "upstream": {
+                       "nodes": {
+                               "172.16.238.20:1980": 1
+                       },
+                       "type": "roundrobin"
+               }
+       }`
+
+       exportR := `{
+               "components": {},
+               "info": {
+                       "title": "RoutesExport",
+                       "version": "3.0.0"
+               },
+               "openapi": "3.0.0",
+               "paths": {
+                       "/hello": {
+                               "get": {
+                                       "operationId": "route_allGET",
+                                       "requestBody": {},
+                                       "responses": {
+                                               "default": {
+                                                       "description": ""
+                                               }
+                                       },
+                                       "summary": "所有",
+                                       "x-apisix-enable_websocket": false,
+                                       "x-apisix-labels": {
+                                               "API_VERSION": "v1",
+                                               "test": "1"
+                                       },
+                                       "x-apisix-priority": 0,
+                                       "x-apisix-status": 1,
+                                       "x-apisix-upstream": {
+                                               "nodes": {
+                                                       "172.16.238.20:1980": 1
+                                               },
+                                               "type": "roundrobin"
+                                       }
+                               }
+                       }
+               }
+       }`
+       var route *entity.Route
+       var service *entity.Service
+       err := json.Unmarshal([]byte(r), &route)
+       err = json.Unmarshal([]byte(s), &service)
+
+       mStore := &store.MockInterface{}
+       mStore.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(route, nil)
+
+       mStoreService := &store.MockInterface{}
+       mStoreService.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(service, nil)
+
+       h := Handler{routeStore: mStore, serviceStore: mStoreService}
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       ret, err := h.ExportRoutes(ctx)
+       assert.Nil(t, err)
+       _ret, err := json.Marshal(ret)
+       if err != nil {
+       }
+       assert.Equal(t, replaceStr(exportR), string(_ret))
+       assert.NotNil(t, _ret)
+}
+
+// 10.Create a service with label data and a route without label data, and 
export the route.
+//  Label is the data of the service
+func TestExportRoutesCreateByLabel2(t *testing.T) {
+       input := &ExportInput{IDs: "1"}
+       s := `{
+               "name": "testservice",
+               "desc": "testservice_desc",
+               "enable_websocket":true,
+               "labels": {
+                       "build": "16", 
+                       "env": "production", 
+                       "version": "v2"
+               }
+       }`
+
+       r := `{
+               "name": "route_all",
+               "desc": "所有",
+               "status": 1,
+               "methods": ["GET"],
+               "service_id": "s2",
+               "vars": [
+                       ["arg_name", "==", "test"]
+               ],
+               "uri": "/hello"
+       }`
+
+       exportR := `{
+               "components": {},
+               "info": {
+                       "title": "RoutesExport",
+                       "version": "3.0.0"
+               },
+               "openapi": "3.0.0",
+               "paths": {
+                       "/hello": {
+                               "get": {
+                                       "operationId": "route_allGET",
+                                       "requestBody": {},
+                                       "responses": {
+                                               "default": {
+                                                       "description": ""
+                                               }
+                                       },
+                                       "summary": "所有",
+                                       "x-apisix-enable_websocket": false,
+                                       "x-apisix-labels": {
+                                               "build": "16",
+                                               "env": "production",
+                                               "version": "v2"
+                                       },
+                                       "x-apisix-priority": 0,
+                                       "x-apisix-status": 1,
+                                       "x-apisix-vars": [
+                                               ["arg_name", "==", "test"]
+                                       ]
+                               }
+                       }
+               }
+       }`
+       var route *entity.Route
+       var service *entity.Service
+       err := json.Unmarshal([]byte(r), &route)
+       err = json.Unmarshal([]byte(s), &service)
+
+       mStore := &store.MockInterface{}
+       mStore.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(route, nil)
+
+       mStoreService := &store.MockInterface{}
+       mStoreService.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(service, nil)
+
+       h := Handler{routeStore: mStore, serviceStore: mStoreService}
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       ret, err := h.ExportRoutes(ctx)
+       assert.Nil(t, err)
+       _ret, err := json.Marshal(ret)
+       if err != nil {
+       }
+       assert.Equal(t, replaceStr(exportR), string(_ret))
+       assert.NotNil(t, _ret)
+}
+
+// 11.Test export route request_ validation data correctness
+func TestExportRoutesCreateByRequestValidation(t *testing.T) {
+       input := &ExportInput{IDs: "1"}
+       r := `{
+               "uris": ["/test-test"],
+               "name": "route_all",
+               "desc": "所有",
+               "methods": ["GET"],
+               "hosts": ["test.com"],
+               "plugins": {
+                       "request-validation": {
+                               "body_schema": {
+                                       "properties": {
+                                               "boolean_payload": {
+                                                       "type": "boolean"
+                                               },
+                                               "required_payload": {
+                                                       "type": "string"
+                                               }
+                                       },
+                                       "required": ["required_payload"],
+                                       "type": "object"
+                               },
+                               "disable": false,
+                               "header_schema": {
+                                       "properties": {
+                                               "test": {
+                                                       "enum": "test-enum",
+                                                       "type": "string"
+                                               }
+                                       },
+                                       "type": "string"
+                               }
+                       }
+               },
+               "status": 1
+       }`
+
+       exportR := `{
+               "components": {},
+               "info": {
+                       "title": "RoutesExport",
+                       "version": "3.0.0"
+               },
+               "openapi": "3.0.0",
+               "paths": {
+                       "/test-test": {
+                               "get": {
+                                       "operationId": "route_allGET",
+                                       "parameters": [{
+                                               "in": "header",
+                                               "name": "test",
+                                               "schema": {
+                                                       "type": "string"
+                                               }
+                                       }],
+                                       "requestBody": {
+                                               "content": {
+                                                       "*/*": {
+                                                               "schema": {
+                                                                       
"properties": {
+                                                                               
"boolean_payload": {
+                                                                               
        "type": "boolean"
+                                                                               
},
+                                                                               
"required_payload": {
+                                                                               
        "type": "string"
+                                                                               
}
+                                                                       },
+                                                                       
"required": ["required_payload"],
+                                                                       "type": 
"object"
+                                                               }
+                                                       }
+                                               }
+                                       },
+                                       "responses": {
+                                               "default": {
+                                                       "description": ""
+                                               }
+                                       },
+                                       "security": [],
+                                       "summary": "所有",
+                                       "x-apisix-enable_websocket": false,
+                                       "x-apisix-hosts": ["test.com"],
+                                       "x-apisix-plugins": {},
+                                       "x-apisix-priority": 0,
+                                       "x-apisix-status": 1
+                               }
+                       }
+               }
+       }`
+       var route *entity.Route
+       err := json.Unmarshal([]byte(r), &route)
+
+       mStore := &store.MockInterface{}
+       mStore.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(route, nil)
+
+       h := Handler{routeStore: mStore}
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       ret, err := h.ExportRoutes(ctx)
+       assert.Nil(t, err)
+       _ret, err := json.Marshal(ret)
+       if err != nil {
+       }
+       assert.Equal(t, replaceStr(exportR), string(_ret))
+       assert.NotNil(t, _ret)
+}
+
+// 12.Export route create by jwt-auth plugin
+func TestExportRoutesCreateByJWTAuth(t *testing.T) {
+       input := &ExportInput{IDs: "1"}
+       r := `{
+               "uri": "/hello",
+               "plugins": {
+                       "jwt-auth": {}
+               },
+               "upstream": {
+                       "type": "roundrobin",
+                  "nodes": [{
+                          "host": "172.16.238.20",
+                          "port": 1980,
+                          "weight": 1
+                  }]
+               }
+       }`
+
+       c := `{
+               "username": "jack",
+               "plugins": {
+                       "jwt-auth": {
+                               "key": "user-key",
+                               "secret": "my-secret-key",
+                               "algorithm": "HS256"
+                       }
+               },
+               "desc": "test description"
+       }`
+
+       exportR := `{
+               "components": {
+                       "securitySchemes": {
+                               "bearerAuth": {
+                                       "bearerFormat": "JWT",
+                                       "scheme": "bearer",
+                                       "type": "http"
+                               }
+                       }
+               },
+               "info": {
+                       "title": "RoutesExport",
+                       "version": "3.0.0"
+               },
+               "openapi": "3.0.0",
+               "paths": {
+                       "/hello": {}
+               }
+       }`
+
+       var route *entity.Route
+       var consumer *entity.Consumer
+       err := json.Unmarshal([]byte(r), &route)
+       err = json.Unmarshal([]byte(c), &consumer)
+
+       mStore := &store.MockInterface{}
+       mStore.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(route, nil)
+
+       mStoreConsumer := &store.MockInterface{}
+       mStoreConsumer.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(consumer, nil)
+
+       h := Handler{routeStore: mStore, consumerStore: mStoreConsumer}
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       ret, err := h.ExportRoutes(ctx)
+       assert.Nil(t, err)
+       _ret, err := json.Marshal(ret)
+       if err != nil {
+       }
+       assert.Equal(t, replaceStr(exportR), string(_ret))
+       assert.NotNil(t, _ret)
+}
+
+// 13.Export route create by apikey-auth plugin  basic-auth plugin
+func TestExportRoutesCreateByKeyAuthAndBasicAuth(t *testing.T) {
+       input := &ExportInput{IDs: "1"}
+       r := `{
+               "uri": "/hello",
+               "plugins": {
+                       "key-auth": {},
+                       "basic-auth": {}
+               },
+               "upstream": {
+                       "type": "roundrobin",
+                  "nodes": [{
+                          "host": "172.16.238.20",
+                          "port": 1980,
+                          "weight": 1
+                  }]
+               }
+       }`
+
+       c := `{
+               "username": "jack",
+               "plugins": {
+                       "key-auth": {
+                               "key": "auth-one"
+                       },
+                       "basic-auth": {
+                               "username": "jack",
+                               "password": "123456"
+                       }
+               },
+               "desc": "test description"
+       }`
+
+       exportR := `{
+               "components": {
+                       "securitySchemes": {
+                               "api_key": {
+                                       "in": "header",
+                                       "name": "X-XSRF-TOKEN",
+                                       "type": "apiKey"
+                               },
+                               "basicAuth": {
+                                       "in": "header",
+                                       "name": "basicAuth",
+                                       "type": "basicAuth"
+                               }
+                       }
+               },
+               "info": {
+                       "title": "RoutesExport",
+                       "version": "3.0.0"
+               },
+               "openapi": "3.0.0",
+               "paths": {
+                       "/hello": {}
+               }
+       }`
+
+       var route *entity.Route
+       var consumer *entity.Consumer
+       err := json.Unmarshal([]byte(r), &route)
+       err = json.Unmarshal([]byte(c), &consumer)
+
+       mStore := &store.MockInterface{}
+       mStore.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(route, nil)
+
+       mStoreConsumer := &store.MockInterface{}
+       mStoreConsumer.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(consumer, nil)
+
+       h := Handler{routeStore: mStore, consumerStore: mStoreConsumer}
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       ret, err := h.ExportRoutes(ctx)
+       assert.Nil(t, err)
+       _ret, err := json.Marshal(ret)
+       if err != nil {
+       }
+       assert.Equal(t, replaceStr(exportR), string(_ret))
+       assert.NotNil(t, _ret)
+}
+
+// 14.Export all routes
+func TestExportRoutesAll(t *testing.T) {
+       input := &store.ListInput{}
+       //*entity.Route
+       r1 := `{
+               "name": "aaaa",
+               "status": 1,
+               "uri": "/hello_",
+               "host":  "*.bar.com",
+               "methods": [ "POST"],
+               "upstream": {
+                       "nodes": {
+                               "172.16.238.20:1980": 1
+                       },
+                       "type": "roundrobin"
+               }
+       }`
+
+       r2 := `{
+               "name": "aaaa2",
+               "status": 1,
+               "uris": ["/hello_2"],
+               "hosts": ["foo.com", "*.bar.com"],
+               "methods": ["GET"],
+               "upstream": {
+                       "nodes": {
+                               "172.16.238.20:1980": 1
+                       },
+                       "type": "roundrobin"
+               }
+       }`
+
+       exportR1 := `{
+               "components": {},
+               "info": {
+                       "title": "RoutesExport",
+                       "version": "3.0.0"
+               },
+               "openapi": "3.0.0",
+               "paths": {
+                       "/hello_": {
+                               "post": {
+                                       "operationId": "aaaaPOST",
+                                       "requestBody": {},
+                                       "responses": {
+                                               "default": {
+                                                       "description": ""
+                                               }
+                                       },
+                                       "x-apisix-enable_websocket": false,
+                                       "x-apisix-host": "*.bar.com",
+                                       "x-apisix-plugins": {},
+                                       "x-apisix-priority": 0,
+                                       "x-apisix-status": 1,
+                                       "x-apisix-upstream": {
+                                               "nodes": {
+                                                       "172.16.238.20:1980": 1
+                                               },
+                                               "type": "roundrobin"
+                                       }
+                               }
+                       },
+                       "/hello_2": {
+                               "get": {
+                                       "operationId": "aaaa2GET",
+                                       "requestBody": {},
+                                       "responses": {
+                                               "default": {
+                                                       "description": ""
+                                               }
+                                       },
+                                       "x-apisix-enable_websocket": false,
+                                       "x-apisix-hosts": ["foo.com", 
"*.bar.com"],
+                                       "x-apisix-plugins": {},
+                                       "x-apisix-priority": 0,
+                                       "x-apisix-status": 1,
+                                       "x-apisix-upstream": {
+                                               "nodes": {
+                                                       "172.16.238.20:1980": 1
+                                               },
+                                               "type": "roundrobin"
+                                       }
+                               }
+                       }
+               }
+       }`
+       var route *entity.Route
+       var route2 *entity.Route
+       var routes []*entity.Route
+       err := json.Unmarshal([]byte(r1), &route)
+       err = json.Unmarshal([]byte(r2), &route2)
+       mStore := &store.MockInterface{}
+       getCalled := false
+
+       routes = append(routes, route)
+       routes = append(routes, route2)
+
+       mStore.On("List", mock.Anything).Run(func(args mock.Arguments) {
+               getCalled = true
+       }).Return(func(input store.ListInput) *store.ListOutput {
+               var returnData []interface{}
+               for _, c := range routes {
+                       returnData = append(returnData, c)
+               }
+               return &store.ListOutput{
+                       Rows:      returnData,
+                       TotalSize: len(returnData),
+               }
+       }, nil)
+
+       h := Handler{routeStore: mStore}
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       ret, err := h.ExportAllRoutes(ctx)
+       assert.Nil(t, err)
+       ret1, err := json.Marshal(ret)
+       if err != nil {
+       }
+       assert.Equal(t, replaceStr(exportR1), string(ret1))
+       assert.NotNil(t, ret1)
+       assert.True(t, getCalled)
+}
+
+//15.Create service according to upstream1 ID
+// Create route according to upstream2 ID and service ID
+func TestExportRoutesCreateByUpstreamIDAndServiceID2(t *testing.T) {
+       input := &ExportInput{IDs: "1"}
+       us := `{
+               "id": "u1",
+               "nodes": [
+                       {
+                               "host": "172.16.238.20",
+                               "port": 1980,
+                               "weight": 1
+                       }
+               ],
+               "type": "roundrobin"
+       }`
+
+       us2 := `{
+               "id": "u2",
+               "nodes": [
+                       {
+                               "host": "172.16.238.20",
+                               "port": 1981,
+                               "weight": 1
+                       }
+               ],
+               "type": "roundrobin"
+       }`
+
+       s := `{
+               "id": "s1",
+               "name": "testservice",
+               "desc": "testservice_desc",
+               "enable_websocket":true,
+               "upstream_id": "u2"
+       }`
+
+       r := `{
+               "name": "route_all",
+               "desc": "所有",
+               "status": 1,
+               "methods": ["GET"],
+               "priority": 0,
+               "service_id": "s1",
+               "labels": {
+                       "test": "1",
+                       "API_VERSION": "v1"
+               },
+               "vars": [
+                       ["arg_name", "==", "test"]
+               ],
+               "uri": "/hello",
+               "enable_websocket":false,
+               "plugins": { 
+                       "prometheus": {
+                               "disable": false
+                       }
+               },
+               "upstream_id": "u1"
+       }`
+
+       exportR := `{
+               "components": {},
+               "info": {
+                       "title": "RoutesExport",
+                       "version": "3.0.0"
+               },
+               "openapi": "3.0.0",
+               "paths": {
+                       "/hello": {
+                               "get": {
+                                       "operationId": "route_allGET",
+                                       "requestBody": {},
+                                       "responses": {
+                                               "default": {
+                                                       "description": ""
+                                               }
+                                       },
+                                       "security": [],
+                                       "summary": "所有",
+                                       "x-apisix-enable_websocket": false,
+                                       "x-apisix-labels": {
+                                               "API_VERSION": "v1",
+                                               "test": "1"
+                                       },
+                                       "x-apisix-plugins": {
+                                               "prometheus": {
+                                                       "disable": false
+                                               }
+                                       },
+                                       "x-apisix-priority": 0,
+                                       "x-apisix-status": 1,
+                                       "x-apisix-upstream": {
+                                               "id": "u1",
+                                               "nodes": [{
+                                                       "host": "172.16.238.20",
+                                                       "port": 1980,
+                                                       "weight": 1
+                                               }],
+                                               "type": "roundrobin"
+                                       },
+                                       "x-apisix-vars": [
+                                               ["arg_name", "==", "test"]
+                                       ]
+                               }
+                       }
+               }
+       }`
+       var route *entity.Route
+       var service *entity.Service
+       var upstream *entity.Upstream
+       var upstream2 *entity.Upstream
+       var upstreams []*entity.Upstream
+
+       err := json.Unmarshal([]byte(r), &route)
+       err = json.Unmarshal([]byte(s), &service)
+       err = json.Unmarshal([]byte(us), &upstream)
+       err = json.Unmarshal([]byte(us2), &upstream2)
+
+       mStore := &store.MockInterface{}
+       mStore.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(route, nil)
+
+       upstreams = append(upstreams, upstream)
+       upstreams = append(upstreams, upstream2)
+       getCalled := true
+
+       mStoreUpstream := &store.MockInterface{}
+       mStoreUpstream.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(upstream, nil)
+
+       mStoreUpstream.On("List", mock.Anything).Run(func(args mock.Arguments) {
+               getCalled = true
+       }).Return(func(input store.ListInput) *store.ListOutput {
+               var returnData []interface{}
+               for _, c := range upstreams {
+                       returnData = append(returnData, c)
+               }
+               return &store.ListOutput{
+                       Rows:      returnData,
+                       TotalSize: len(returnData),
+               }
+       }, nil)
+
+       mStoreService := &store.MockInterface{}
+       mStoreService.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(service, nil)
+
+       h := Handler{routeStore: mStore, upstreamStore: mStoreUpstream, 
serviceStore: mStoreService}
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       ret, err := h.ExportRoutes(ctx)
+       assert.Nil(t, err)
+       _ret, err := json.Marshal(ret)
+       if err != nil {
+       }
+       assert.Equal(t, replaceStr(exportR), string(_ret))
+       assert.NotNil(t, _ret)
+       assert.True(t, getCalled)
+}
+
+// 16.Add suffix when testing the same URI export "APISIX-REPEAT-URI-" + 
Millisecond time stamp:  "APISIX-REPEAT-URI-1257894000000"
+func TestExportRoutesSameURI(t *testing.T) {
+       input := &store.ListInput{}
+       //*entity.Route
+       r1 := `{
+               "uris": ["/test-test"],
+               "name": "route_all",
+               "desc": "所有",
+               "methods": ["GET"],
+               "hosts": ["test.com"],
+               "status": 1,
+               "upstream": {
+                       "nodes": {
+                               "172.16.238.20:1980": 1
+                       },
+                       "type": "roundrobin"
+               }
+}`
+
+       r2 := `{
+               "uris": ["/test-test"],
+               "name": "route_all",
+               "desc": "所有",
+               "methods": ["GET"],
+               "hosts": ["test.com"],
+               "status": 1,
+               "upstream": {
+                       "nodes": {
+                               "172.16.238.20:1980": 1
+                       },
+                       "type": "roundrobin"
+               }
+}`
+
+       exportR1 := `{
+               "components": {},
+               "info": {
+                       "title": "RoutesExport",
+                       "version": "3.0.0"
+               },
+               "openapi": "3.0.0",
+               "paths": {
+                       "/test-test": {
+                               "get": {
+                                       "operationId": "route_allGET",
+                                       "requestBody": {},
+                                       "responses": {
+                                               "default": {
+                                                       "description": ""
+                                               }
+                                       },
+                                       "summary": "所有",
+                                       "x-apisix-enable_websocket": false,
+                                       "x-apisix-hosts": ["test.com"],
+                                       "x-apisix-plugins": {},
+                                       "x-apisix-priority": 0,
+                                       "x-apisix-status": 1,
+                                       "x-apisix-upstream": {
+                                               "nodes": {
+                                                       "172.16.238.20:1980": 1
+                                               },
+                                               "type": "roundrobin"
+                                       }
+                               }
+                       },
+                       "/test-test-APISIX-REPEAT-URI-`
+       var route *entity.Route
+       var route2 *entity.Route
+       var routes []*entity.Route
+       err := json.Unmarshal([]byte(r1), &route)
+       err = json.Unmarshal([]byte(r2), &route2)
+       mStore := &store.MockInterface{}
+       getCalled := false
+
+       routes = append(routes, route)
+       routes = append(routes, route2)
+
+       mStore.On("List", mock.Anything).Run(func(args mock.Arguments) {
+               getCalled = true
+       }).Return(func(input store.ListInput) *store.ListOutput {
+               var returnData []interface{}
+               for _, c := range routes {
+                       returnData = append(returnData, c)
+               }
+               return &store.ListOutput{
+                       Rows:      returnData,
+                       TotalSize: len(returnData),
+               }
+       }, nil)
+
+       h := Handler{routeStore: mStore}
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       ret, err := h.ExportAllRoutes(ctx)
+       assert.Nil(t, err)
+       ret1, err := json.Marshal(ret)
+       if err != nil {
+       }

Review comment:
       done




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to