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

liuxiran pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git


The following commit(s) were added to refs/heads/master by this push:
     new 6ee8dd1  feat: import route from OpenAPI Specification3.0 (#1102)
6ee8dd1 is described below

commit 6ee8dd15f22e10182a27cbfa4a69b56a3b0f9b43
Author: JinChen <[email protected]>
AuthorDate: Wed Jan 27 17:48:02 2021 +0800

    feat: import route from OpenAPI Specification3.0 (#1102)
---
 api/cmd/manager/main_test.go                       |   2 +
 api/go.mod                                         |   3 +-
 api/go.sum                                         |  26 +-
 api/internal/conf/conf.go                          |   1 +
 api/internal/core/entity/entity.go                 |  12 +-
 api/internal/core/store/store.go                   |  24 +-
 api/internal/core/store/validate.go                |   9 +-
 api/internal/filter/schema.go                      |   1 -
 api/internal/handler/data_loader/route_import.go   | 522 +++++++++++++++++++
 .../handler/data_loader/route_import_test.go       | 178 +++++++
 api/internal/handler/handler_test.go               |   2 +-
 api/internal/route.go                              |   1 +
 api/internal/utils/utils.go                        |  37 ++
 api/test/e2e/base.go                               |   5 +-
 api/test/e2e/http.go                               | 106 ++++
 api/test/e2e/route_export_test.go                  |   5 +-
 api/test/e2e/route_import_test.go                  | 579 +++++++++++++++++++++
 api/test/e2e/route_online_debug_test.go            |   2 +-
 api/test/e2e/route_with_plugin_jwt_test.go         |   4 +-
 api/test/testdata/import/default.json              |  39 ++
 api/test/testdata/import/default.yaml              |  44 ++
 api/test/testdata/import/multi-routes.yaml         | 224 ++++++++
 api/test/testdata/import/with-plugins.yaml         |  80 +++
 api/test/testdata/import/with-service-id.yaml      |  39 ++
 api/test/testdata/import/with-upstream-id.yaml     |  39 ++
 25 files changed, 1949 insertions(+), 35 deletions(-)

diff --git a/api/cmd/manager/main_test.go b/api/cmd/manager/main_test.go
index 1e7b51d..f891490 100644
--- a/api/cmd/manager/main_test.go
+++ b/api/cmd/manager/main_test.go
@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 package main
+
 import (
        "os"
        "os/signal"
@@ -22,6 +23,7 @@ import (
        "syscall"
        "testing"
 )
+
 func TestMainWrapper(t *testing.T) {
        if os.Getenv("ENV") == "test" {
                t.Skip("skipping build binary when execute unit test")
diff --git a/api/go.mod b/api/go.mod
index d1681ff..e0b9457 100644
--- a/api/go.mod
+++ b/api/go.mod
@@ -17,6 +17,7 @@ require (
        github.com/dgrijalva/jwt-go v3.2.0+incompatible
        github.com/dustin/go-humanize v1.0.0 // indirect
        github.com/evanphx/json-patch/v5 v5.1.0
+       github.com/getkin/kin-openapi v0.33.0
        github.com/gin-contrib/pprof v1.3.0
        github.com/gin-contrib/sessions v0.0.3
        github.com/gin-contrib/static v0.0.0-20200916080430-d45d9a37d28e
@@ -29,7 +30,7 @@ require (
        github.com/jonboulle/clockwork v0.2.2 // indirect
        github.com/prometheus/client_golang v1.8.0 // indirect
        github.com/satori/go.uuid v1.2.0
-       github.com/shiningrush/droplet v0.2.6-0.20210126131015-cbf9557974f7
+       github.com/shiningrush/droplet v0.2.6-0.20210127040147-53817015cd1b
        github.com/shiningrush/droplet/wrapper/gin v0.2.1
        github.com/sirupsen/logrus v1.7.0 // indirect
        github.com/sony/sonyflake v1.0.0
diff --git a/api/go.sum b/api/go.sum
index 541e882..d8234a3 100644
--- a/api/go.sum
+++ b/api/go.sum
@@ -82,6 +82,10 @@ github.com/fatih/color v1.7.0/go.mod 
h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
 github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod 
h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
 github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod 
h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
 github.com/fsnotify/fsnotify v1.4.7/go.mod 
h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/getkin/kin-openapi v0.33.0 
h1:KccukV3/1h95R0OP7vfWB3KVy9lxA5i8i3BwlA3tRpE=
+github.com/getkin/kin-openapi v0.33.0/go.mod 
h1:ZJSfy1PxJv2QQvH9EdBj3nupRTVvV42mkW6zKUlRBwk=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
+github.com/ghodss/yaml v1.0.0/go.mod 
h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/ghodss/yaml v1.0.0/go.mod 
h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/gin-contrib/pprof v1.3.0 
h1:G9eK6HnbkSqDZBYbzG4wrjCsA4e+cvYAHUZw6W+W9K0=
 github.com/gin-contrib/pprof v1.3.0/go.mod 
h1:waMjT1H9b179t3CxuG1cV3DHpga6ybizwfBaM5OXaB0=
@@ -103,6 +107,10 @@ github.com/go-kit/kit v0.10.0/go.mod 
h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgO
 github.com/go-logfmt/logfmt v0.3.0/go.mod 
h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod 
h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-logfmt/logfmt v0.5.0/go.mod 
h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-openapi/jsonpointer v0.19.5 
h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
+github.com/go-openapi/jsonpointer v0.19.5/go.mod 
h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/swag v0.19.5 
h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
+github.com/go-openapi/swag v0.19.5/go.mod 
h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
 github.com/go-playground/assert/v2 v2.0.1 
h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
 github.com/go-playground/assert/v2 v2.0.1/go.mod 
h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/locales v0.12.1/go.mod 
h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
@@ -138,7 +146,6 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod 
h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
 github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod 
h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
 github.com/golang/protobuf v1.4.0/go.mod 
h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
 github.com/golang/protobuf v1.4.1/go.mod 
h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.4.2 
h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
 github.com/golang/protobuf v1.4.2/go.mod 
h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.4.3 
h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
 github.com/golang/protobuf v1.4.3/go.mod 
h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
@@ -235,6 +242,9 @@ github.com/leodido/go-urn v1.2.0 
h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
 github.com/leodido/go-urn v1.2.0/go.mod 
h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
 github.com/lightstep/lightstep-tracer-common/golang/gogo 
v0.0.0-20190605223551-bc2310a04743/go.mod 
h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
 github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod 
h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod 
h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e 
h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod 
h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mattn/go-colorable v0.0.9/go.mod 
h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-isatty v0.0.3/go.mod 
h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-isatty v0.0.4/go.mod 
h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
@@ -290,7 +300,6 @@ github.com/performancecopilot/speed 
v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9
 github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod 
h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod 
h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
 github.com/pkg/errors v0.8.0/go.mod 
h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod 
h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod 
h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -308,7 +317,6 @@ github.com/prometheus/client_golang v1.8.0/go.mod 
h1:O9VU6huf47PktckDQfMTX0Y8tY0
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod 
h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod 
h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod 
h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 
h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod 
h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.1.0/go.mod 
h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.2.0 
h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
@@ -339,8 +347,8 @@ github.com/satori/go.uuid v1.2.0/go.mod 
h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod 
h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/shiningrush/droplet v0.2.4 
h1:OW4Pp+dXs9O61QKTiYSRWCdQeOyzO1n9h+i2PDJ5DK0=
 github.com/shiningrush/droplet v0.2.4/go.mod 
h1:akW2vIeamvMD6zj6wIBfzYn6StGXBxwlW3gA+hcHu5M=
-github.com/shiningrush/droplet v0.2.6-0.20210126131015-cbf9557974f7 
h1:E0+CduActvXFpdvUXu7wxfw+trl5MKRkY4IZ1uQYsvc=
-github.com/shiningrush/droplet v0.2.6-0.20210126131015-cbf9557974f7/go.mod 
h1:akW2vIeamvMD6zj6wIBfzYn6StGXBxwlW3gA+hcHu5M=
+github.com/shiningrush/droplet v0.2.6-0.20210127040147-53817015cd1b 
h1:kAS+hyJuHUm/lAN4xbKY4/QHbRse95lcjxcIZwSJEvM=
+github.com/shiningrush/droplet v0.2.6-0.20210127040147-53817015cd1b/go.mod 
h1:akW2vIeamvMD6zj6wIBfzYn6StGXBxwlW3gA+hcHu5M=
 github.com/shiningrush/droplet/wrapper/gin v0.2.1 
h1:1o+5KUF2sKsdZ7SkmOC5ahAP1qaZKqnm0c5hOYFV6YQ=
 github.com/shiningrush/droplet/wrapper/gin v0.2.1/go.mod 
h1:cx5BfLuStFDFIKuEOc1zBTpiT3B4Ezkg3MdlP6rW51I=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod 
h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@@ -363,13 +371,13 @@ github.com/spf13/pflag v1.0.1/go.mod 
h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
 github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod 
h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
 github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod 
h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
 github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod 
h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
-github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
 github.com/stretchr/objx v0.1.0/go.mod 
h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
 github.com/stretchr/objx v0.1.1/go.mod 
h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2/go.mod 
h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod 
h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod 
h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod 
h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.6.1 
h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
 github.com/stretchr/testify v1.6.1/go.mod 
h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/tidwall/gjson v1.6.7 h1:Mb1M9HZCRWEcXQ8ieJo7auYyyiSux6w9XN3AdTpxJrE=
@@ -427,7 +435,6 @@ go.uber.org/zap v1.16.0/go.mod 
h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod 
h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod 
h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod 
h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 
h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk=
 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod 
h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod 
h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod 
h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -464,7 +471,6 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod 
h1:t9HGtf8HONx5eT2rtn
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod 
h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod 
h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod 
h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859 
h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod 
h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod 
h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod 
h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -510,7 +516,6 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod 
h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 
h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM=
 golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 
h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88=
@@ -533,7 +538,6 @@ golang.org/x/tools 
v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod 
h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod 
h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 
h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
 golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod 
h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -544,7 +548,6 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a 
h1:CB3a9Nez8M13wwlr/E2Ytwo
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod 
h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 
h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 
h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -574,7 +577,6 @@ google.golang.org/protobuf v1.25.0 
h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4
 google.golang.org/protobuf v1.25.0/go.mod 
h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod 
h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 
h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 
h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/api/internal/conf/conf.go b/api/internal/conf/conf.go
index 883b1a6..cc1a82f 100644
--- a/api/internal/conf/conf.go
+++ b/api/internal/conf/conf.go
@@ -52,6 +52,7 @@ var (
        UserList         = make(map[string]User, 2)
        AuthConf         Authentication
        SSLDefaultStatus = 1 //enable ssl by default
+       ImportSizeLimit  = 10 * 1024 * 1024
        PIDPath          = "/tmp/manager-api.pid"
 )
 
diff --git a/api/internal/core/entity/entity.go 
b/api/internal/core/entity/entity.go
index 7463bee..f0b4edf 100644
--- a/api/internal/core/entity/entity.go
+++ b/api/internal/core/entity/entity.go
@@ -171,12 +171,6 @@ type UpstreamDef struct {
        Labels       map[string]string `json:"labels,omitempty"`
 }
 
-type RequestValidation struct {
-       Type       string      `json:"type,omitempty"`
-       Required   []string    `json:"required,omitempty"`
-       Properties interface{} `json:"properties,omitempty"`
-}
-
 // swagger:model Upstream
 type Upstream struct {
        BaseInfo
@@ -241,6 +235,12 @@ type Script struct {
        Script interface{} `json:"script,omitempty"`
 }
 
+type RequestValidation struct {
+       Type       string      `json:"type,omitempty"`
+       Required   []string    `json:"required,omitempty"`
+       Properties interface{} `json:"properties,omitempty"`
+}
+
 // swagger:model GlobalPlugins
 type GlobalPlugins struct {
        BaseInfo
diff --git a/api/internal/core/store/store.go b/api/internal/core/store/store.go
index 2da0572..2b0031c 100644
--- a/api/internal/core/store/store.go
+++ b/api/internal/core/store/store.go
@@ -221,7 +221,7 @@ func (s *GenericStore) List(_ context.Context, input 
ListInput) (*ListOutput, er
 func (s *GenericStore) ingestValidate(obj interface{}) (err error) {
        if s.opt.Validator != nil {
                if err := s.opt.Validator.Validate(obj); err != nil {
-                       log.Errorf("data validate failed: %s", err)
+                       log.Errorf("data validate failed: %s, %v", err, obj)
                        return err
                }
        }
@@ -237,7 +237,8 @@ func (s *GenericStore) ingestValidate(obj interface{}) (err 
error) {
        return err
 }
 
-func (s *GenericStore) Create(ctx context.Context, obj interface{}) 
(interface{}, error) {
+func (s *GenericStore) CreateCheck(obj interface{}) ([]byte, error) {
+
        if setter, ok := obj.(entity.BaseInfoSetter); ok {
                info := setter.GetBaseInfo()
                info.Creating()
@@ -257,12 +258,27 @@ func (s *GenericStore) Create(ctx context.Context, obj 
interface{}) (interface{}
                return nil, fmt.Errorf("key: %s is conflicted", key)
        }
 
-       bs, err := json.Marshal(obj)
+       bytes, err := json.Marshal(obj)
        if err != nil {
                log.Errorf("json marshal failed: %s", err)
                return nil, fmt.Errorf("json marshal failed: %s", err)
        }
-       if err := s.Stg.Create(ctx, s.GetObjStorageKey(obj), string(bs)); err 
!= nil {
+
+       return bytes, nil
+}
+
+func (s *GenericStore) Create(ctx context.Context, obj interface{}) 
(interface{}, error) {
+       if setter, ok := obj.(entity.BaseInfoSetter); ok {
+               info := setter.GetBaseInfo()
+               info.Creating()
+       }
+
+       bytes, err := s.CreateCheck(obj)
+       if err != nil {
+               return nil, err
+       }
+
+       if err := s.Stg.Create(ctx, s.GetObjStorageKey(obj), string(bytes)); 
err != nil {
                return nil, err
        }
 
diff --git a/api/internal/core/store/validate.go 
b/api/internal/core/store/validate.go
index 493c169..abf5933 100644
--- a/api/internal/core/store/validate.go
+++ b/api/internal/core/store/validate.go
@@ -71,7 +71,8 @@ func (v *JsonSchemaValidator) Validate(obj interface{}) error 
{
 }
 
 type APISIXJsonSchemaValidator struct {
-       schema *gojsonschema.Schema
+       schema    *gojsonschema.Schema
+       schemaDef string
 }
 
 func NewAPISIXJsonSchemaValidator(jsonPath string) (Validator, error) {
@@ -87,7 +88,8 @@ func NewAPISIXJsonSchemaValidator(jsonPath string) 
(Validator, error) {
                return nil, fmt.Errorf("new schema failed: %s", err)
        }
        return &APISIXJsonSchemaValidator{
-               schema: s,
+               schema:    s,
+               schemaDef: schemaDef,
        }, nil
 }
 
@@ -231,7 +233,7 @@ func checkConf(reqBody interface{}) error {
 func (v *APISIXJsonSchemaValidator) Validate(obj interface{}) error {
        ret, err := v.schema.Validate(gojsonschema.NewGoLoader(obj))
        if err != nil {
-               log.Errorf("schema validate failed: %s", err)
+               log.Errorf("schema validate failed: %s, s: %v, obj: %v", err, 
v.schema, obj)
                return fmt.Errorf("schema validate failed: %s", err)
        }
 
@@ -243,6 +245,7 @@ func (v *APISIXJsonSchemaValidator) Validate(obj 
interface{}) error {
                        }
                        errString.AppendString(vErr.String())
                }
+               log.Errorf("schema validate failed:s: %v, obj: %#v", 
v.schemaDef, obj)
                return fmt.Errorf("schema validate failed: %s", 
errString.String())
        }
 
diff --git a/api/internal/filter/schema.go b/api/internal/filter/schema.go
index e9d50f2..120d8bc 100644
--- a/api/internal/filter/schema.go
+++ b/api/internal/filter/schema.go
@@ -178,7 +178,6 @@ func SchemaCheck() gin.HandlerFunc {
        return func(c *gin.Context) {
                pathPrefix := "/apisix/admin/"
                resource := strings.TrimPrefix(c.Request.URL.Path, pathPrefix)
-
                idx := strings.LastIndex(resource, "/")
                if idx > 1 {
                        resource = resource[:idx]
diff --git a/api/internal/handler/data_loader/route_import.go 
b/api/internal/handler/data_loader/route_import.go
new file mode 100644
index 0000000..d1ed425
--- /dev/null
+++ b/api/internal/handler/data_loader/route_import.go
@@ -0,0 +1,522 @@
+/*
+ * 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 (
+       "bytes"
+       "context"
+       "encoding/json"
+       "errors"
+       "fmt"
+       "net/http"
+       "path"
+       "reflect"
+       "regexp"
+       "strings"
+
+       "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"
+
+       "github.com/apisix/manager-api/internal/conf"
+       "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/apisix/manager-api/internal/utils/consts"
+)
+
+type ImportHandler struct {
+       routeStore    *store.GenericStore
+       svcStore      store.Interface
+       upstreamStore store.Interface
+}
+
+func NewImportHandler() (handler.RouteRegister, error) {
+       return &ImportHandler{
+               routeStore:    store.GetStore(store.HubKeyRoute),
+               svcStore:      store.GetStore(store.HubKeyService),
+               upstreamStore: store.GetStore(store.HubKeyUpstream),
+       }, nil
+}
+
+
+var regPathVar = regexp.MustCompile(`{[\w.]*}`)
+var regPathRepeat = regexp.MustCompile(`-APISIX-REPEAT-URI-[\d]*`)
+
+
+func (h *ImportHandler) ApplyRoute(r *gin.Engine) {
+       r.POST("/apisix/admin/import/routes", wgin.Wraps(h.Import,
+               wrapper.InputType(reflect.TypeOf(ImportInput{}))))
+}
+
+type ImportInput struct {
+       Force       bool   `auto_read:"force,query"`
+       FileName    string `auto_read:"_file"`
+       FileContent []byte `auto_read:"file"`
+}
+
+func (h *ImportHandler) Import(c droplet.Context) (interface{}, error) {
+       input := c.Input().(*ImportInput)
+       Force := input.Force
+
+       // file check
+       suffix := path.Ext(input.FileName)
+       if suffix != ".json" && suffix != ".yaml" && suffix != ".yml" {
+               return nil, fmt.Errorf("required file type is .yaml, .yml or 
.json but got: %s", suffix)
+       }
+
+       contentLen := bytes.Count(input.FileContent, nil) - 1
+       if contentLen > conf.ImportSizeLimit {
+               log.Warnf("upload file size exceeds limit: %d", contentLen)
+               return nil, fmt.Errorf("the file size exceeds the limit; limit 
%d", conf.ImportSizeLimit)
+       }
+
+       swagger, err := 
openapi3.NewSwaggerLoader().LoadSwaggerFromData(input.FileContent)
+       if err != nil {
+               return nil, err
+       }
+
+       if len(swagger.Paths) < 1 {
+               return &data.SpecCodeResponse{StatusCode: 
http.StatusBadRequest},
+                       errors.New("empty or invalid imported file")
+       }
+
+       routes, err := OpenAPI3ToRoute(swagger)
+       if err != nil {
+               return nil, err
+       }
+
+       // check route
+       for _, route := range routes {
+               err := checkRouteExist(c.Context(), h.routeStore, route)
+               if err != nil && !Force {
+                       log.Warnf("import duplicate: %s, route: %#v", err, 
route)
+                       return &data.SpecCodeResponse{StatusCode: 
http.StatusBadRequest},
+                               fmt.Errorf("route(uris:%v) conflict, %s", 
route.Uris, err)
+               }
+               if route.ServiceID != nil {
+                       _, err := h.svcStore.Get(c.Context(), 
utils.InterfaceToString(route.ServiceID))
+                       if err != nil {
+                               if err == data.ErrNotFound {
+                                       return 
&data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+                                               fmt.Errorf("service id: %s not 
found", route.ServiceID)
+                               }
+                               return &data.SpecCodeResponse{StatusCode: 
http.StatusBadRequest}, err
+                       }
+               }
+               if route.UpstreamID != nil {
+                       _, err := h.upstreamStore.Get(c.Context(), 
utils.InterfaceToString(route.UpstreamID))
+                       if err != nil {
+                               if err == data.ErrNotFound {
+                                       return 
&data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+                                               fmt.Errorf("upstream id: %s not 
found", route.UpstreamID)
+                               }
+                               return &data.SpecCodeResponse{StatusCode: 
http.StatusBadRequest}, err
+                       }
+               }
+
+               if _, err := h.routeStore.CreateCheck(route); err != nil {
+                       return handler.SpecCodeResponse(err),
+                               fmt.Errorf("create route(uris:%v) failed: %s", 
route.Uris, err)
+               }
+       }
+
+       // create route
+       for _, route := range routes {
+               if Force && route.ID != nil {
+                       if _, err := h.routeStore.Update(c.Context(), route, 
true); err != nil {
+                               return handler.SpecCodeResponse(err),
+                                       fmt.Errorf("update route(uris:%v) 
failed: %s", route.Uris, err)
+                       }
+               } else {
+                       if _, err := h.routeStore.Create(c.Context(), route); 
err != nil {
+                               return handler.SpecCodeResponse(err),
+                                       fmt.Errorf("create route(uris:%v) 
failed: %s", route.Uris, err)
+                       }
+               }
+       }
+
+       return map[string]int{
+               "paths":  len(swagger.Paths),
+               "routes": len(routes),
+       }, nil
+}
+
+func checkRouteExist(ctx context.Context, routeStore *store.GenericStore, 
route *entity.Route) error {
+       //routeStore := store.GetStore(store.HubKeyRoute)
+       ret, err := routeStore.List(ctx, store.ListInput{
+               Predicate: func(obj interface{}) bool {
+                       id := utils.InterfaceToString(route.ID)
+                       item := obj.(*entity.Route)
+                       if id != "" && id != utils.InterfaceToString(item.ID) {
+                               return false
+                       }
+
+                       if !(item.Host == route.Host && item.URI == route.URI 
&& utils.StringSliceEqual(item.Uris, route.Uris) &&
+                               utils.StringSliceEqual(item.RemoteAddrs, 
route.RemoteAddrs) && item.RemoteAddr == route.RemoteAddr &&
+                               utils.StringSliceEqual(item.Hosts, route.Hosts) 
&& item.Priority == route.Priority &&
+                               utils.ValueEqual(item.Vars, route.Vars) && 
item.FilterFunc == route.FilterFunc) {
+                               return false
+                       }
+                       return true
+               },
+               PageSize:   0,
+               PageNumber: 0,
+       })
+       if err != nil {
+               return err
+       }
+       if len(ret.Rows) > 0 {
+               return consts.InvalidParam("route is duplicate")
+       }
+       return nil
+}
+
+func parseExtension(val *openapi3.Operation) (*entity.Route, error) {
+       routeMap := map[string]interface{}{}
+       for key, val := range val.Extensions {
+               if strings.HasPrefix(key, "x-apisix-") {
+                       routeMap[strings.TrimPrefix(key, "x-apisix-")] = val
+               }
+       }
+
+       route := new(entity.Route)
+       routeJson, err := json.Marshal(routeMap)
+       if err != nil {
+               return nil, err
+       }
+
+       err = json.Unmarshal(routeJson, &route)
+       if err != nil {
+               return nil, err
+       }
+
+       return route, nil
+}
+
+type PathValue struct {
+       Method string
+       Value  *openapi3.Operation
+}
+
+func mergePathValue(key string, values []PathValue, swagger *openapi3.Swagger) 
(map[string]*entity.Route, error) {
+       var parsed []PathValue
+       var routes = map[string]*entity.Route{}
+       for _, value := range values {
+               value.Value.OperationID = 
strings.Replace(value.Value.OperationID, value.Method, "", 1)
+               var eq = false
+               for _, v := range parsed {
+                       if utils.ValueEqual(v.Value, value.Value) {
+                               eq = true
+                               if routes[v.Method].Methods == nil {
+                                       routes[v.Method].Methods = []string{}
+                               }
+                               routes[v.Method].Methods = 
append(routes[v.Method].Methods, value.Method)
+                       }
+               }
+               // not equal to the previous ones
+               if !eq {
+                       route, err := getRouteFromPaths(value.Method, key, 
value.Value, swagger)
+                       if err != nil {
+                               return nil, err
+                       }
+                       routes[value.Method] = route
+                       parsed = append(parsed, value)
+               }
+       }
+
+       return routes, nil
+}
+
+func OpenAPI3ToRoute(swagger *openapi3.Swagger) ([]*entity.Route, error) {
+       var routes []*entity.Route
+       paths := swagger.Paths
+       var upstream *entity.UpstreamDef
+       var err error
+       for k, v := range paths {
+               k = regPathRepeat.ReplaceAllString(k, "")
+               upstream = &entity.UpstreamDef{}
+               if up, ok := v.Extensions["x-apisix-upstream"]; ok {
+                       err = json.Unmarshal(up.(json.RawMessage), upstream)
+                       if err != nil {
+                               return nil, err
+                       }
+               }
+
+               var values []PathValue
+               if v.Get != nil {
+                       value := PathValue{
+                               Method: http.MethodGet,
+                               Value:  v.Get,
+                       }
+                       values = append(values, value)
+               }
+               if v.Post != nil {
+                       value := PathValue{
+                               Method: http.MethodPost,
+                               Value:  v.Post,
+                       }
+                       values = append(values, value)
+               }
+               if v.Head != nil {
+                       value := PathValue{
+                               Method: http.MethodHead,
+                               Value:  v.Head,
+                       }
+                       values = append(values, value)
+               }
+               if v.Put != nil {
+                       value := PathValue{
+                               Method: http.MethodPut,
+                               Value:  v.Put,
+                       }
+                       values = append(values, value)
+               }
+               if v.Patch != nil {
+                       value := PathValue{
+                               Method: http.MethodPatch,
+                               Value:  v.Patch,
+                       }
+                       values = append(values, value)
+               }
+               if v.Delete != nil {
+                       value := PathValue{
+                               Method: http.MethodDelete,
+                               Value:  v.Delete,
+                       }
+                       values = append(values, value)
+               }
+
+               // merge same route
+               tmp, err := mergePathValue(k, values, swagger)
+               if err != nil {
+                       return nil, err
+               }
+
+               for _, route := range tmp {
+                       routes = append(routes, route)
+               }
+       }
+
+       return routes, nil
+}
+
+func parseParameters(parameters openapi3.Parameters, plugins 
map[string]interface{}) {
+       props := make(map[string]interface{})
+       var required []string
+       for _, v := range parameters {
+               if v.Value.Schema != nil {
+                       v.Value.Schema.Value.Format = ""
+                       v.Value.Schema.Value.XML = nil
+               }
+
+               switch v.Value.In {
+               case "header":
+                       if v.Value.Schema != nil && v.Value.Schema.Value != nil 
{
+                               props[v.Value.Name] = v.Value.Schema.Value
+                       }
+                       if v.Value.Required {
+                               required = append(required, v.Value.Name)
+                       }
+               }
+       }
+
+       requestValidation := make(map[string]interface{})
+       if rv, ok := plugins["request-validation"]; ok {
+               requestValidation = rv.(map[string]interface{})
+       }
+       requestValidation["header_schema"] = &entity.RequestValidation{
+               Type:       "object",
+               Required:   required,
+               Properties: props,
+       }
+       plugins["request-validation"] = requestValidation
+}
+
+func parseRequestBody(requestBody *openapi3.RequestBodyRef, swagger 
*openapi3.Swagger, plugins map[string]interface{}) {
+       schema := requestBody.Value.Content
+       requestValidation := make(map[string]interface{})
+       if rv, ok := plugins["request-validation"]; ok {
+               requestValidation = rv.(map[string]interface{})
+       }
+       for _, v := range schema {
+               if v.Schema.Ref != "" {
+                       s := getParameters(v.Schema.Ref, 
&swagger.Components).Value
+                       requestValidation["body_schema"] = 
&entity.RequestValidation{
+                               Type:       s.Type,
+                               Required:   s.Required,
+                               Properties: s.Properties,
+                       }
+                       plugins["request-validation"] = requestValidation
+               } else if v.Schema.Value != nil {
+                       if v.Schema.Value.Properties != nil {
+                               for k1, v1 := range v.Schema.Value.Properties {
+                                       if v1.Ref != "" {
+                                               s := getParameters(v1.Ref, 
&swagger.Components)
+                                               v.Schema.Value.Properties[k1] = 
s
+                                       }
+                                       v1.Value.Format = ""
+                               }
+                               requestValidation["body_schema"] = 
&entity.RequestValidation{
+                                       Type:       v.Schema.Value.Type,
+                                       Required:   v.Schema.Value.Required,
+                                       Properties: v.Schema.Value.Properties,
+                               }
+                               plugins["request-validation"] = 
requestValidation
+                       } else if v.Schema.Value.Items != nil {
+                               if v.Schema.Value.Items.Ref != "" {
+                                       s := 
getParameters(v.Schema.Value.Items.Ref, &swagger.Components).Value
+                                       requestValidation["body_schema"] = 
&entity.RequestValidation{
+                                               Type:       s.Type,
+                                               Required:   s.Required,
+                                               Properties: s.Properties,
+                                       }
+                                       plugins["request-validation"] = 
requestValidation
+                               }
+                       } else {
+                               requestValidation["body_schema"] = 
&entity.RequestValidation{
+                                       Type:       "object",
+                                       Required:   []string{},
+                                       Properties: v.Schema.Value.Properties,
+                               }
+                       }
+               }
+               plugins["request-validation"] = requestValidation
+       }
+}
+
+func parseSecurity(security openapi3.SecurityRequirements, securitySchemes 
openapi3.SecuritySchemes, plugins map[string]interface{}) {
+       // todo: import consumers
+       for _, securities := range security {
+               for name := range securities {
+                       if schema, ok := securitySchemes[name]; ok {
+                               value := schema.Value
+                               if value == nil {
+                                       continue
+                               }
+
+                               // basic auth
+                               if value.Type == "http" && value.Scheme == 
"basic" {
+                                       plugins["basic-auth"] = 
map[string]interface{}{}
+                                       //username, ok := 
value.Extensions["username"]
+                                       //if !ok {
+                                       //      continue
+                                       //}
+                                       //password, ok := 
value.Extensions["password"]
+                                       //if !ok {
+                                       //      continue
+                                       //}
+                                       //plugins["basic-auth"] = 
map[string]interface{}{
+                                       //      "username": username,
+                                       //      "password": password,
+                                       //}
+                                       // jwt auth
+                               } else if value.Type == "http" && value.Scheme 
== "bearer" && value.BearerFormat == "JWT" {
+                                       plugins["jwt-auth"] = 
map[string]interface{}{}
+                                       //key, ok := value.Extensions["key"]
+                                       //if !ok {
+                                       //      continue
+                                       //}
+                                       //secret, ok := 
value.Extensions["secret"]
+                                       //if !ok {
+                                       //      continue
+                                       //}
+                                       //plugins["jwt-auth"] = 
map[string]interface{}{
+                                       //      "key":    key,
+                                       //      "secret": secret,
+                                       //}
+                                       // key auth
+                               } else if value.Type == "apiKey" {
+                                       plugins["key-auth"] = 
map[string]interface{}{}
+                                       //key, ok := value.Extensions["key"]
+                                       //if !ok {
+                                       //      continue
+                                       //}
+                                       //plugins["key-auth"] = 
map[string]interface{}{
+                                       //      "key": key,
+                                       //}
+                               }
+                       }
+               }
+       }
+}
+
+func getRouteFromPaths(method, key string, value *openapi3.Operation, swagger 
*openapi3.Swagger) (*entity.Route, error) {
+       // transform /path/{var} to  /path/*
+       foundStr := regPathVar.FindString(key)
+       if foundStr != "" {
+               key = strings.Split(key, foundStr)[0] + "*"
+       }
+
+       route, err := parseExtension(value)
+       if err != nil {
+               return nil, err
+       }
+
+       route.Uris = []string{key}
+       route.Name = value.OperationID
+       route.Desc = value.Summary
+       route.Methods = []string{method}
+
+       if route.Plugins == nil {
+               route.Plugins = make(map[string]interface{})
+       }
+
+       if value.Parameters != nil {
+               parseParameters(value.Parameters, route.Plugins)
+       }
+
+       if value.RequestBody != nil {
+               parseRequestBody(value.RequestBody, swagger, route.Plugins)
+       }
+
+       if value.Security != nil && swagger.Components.SecuritySchemes != nil {
+               parseSecurity(*value.Security, 
swagger.Components.SecuritySchemes, route.Plugins)
+       }
+
+       return route, nil
+}
+
+func getParameters(ref string, components *openapi3.Components) 
*openapi3.SchemaRef {
+       schemaRef := &openapi3.SchemaRef{}
+       arr := strings.Split(ref, "/")
+       if arr[0] == "#" && arr[1] == "components" && arr[2] == "schemas" {
+               schemaRef = components.Schemas[arr[3]]
+               schemaRef.Value.XML = nil
+               // traverse properties to find another ref
+               for k, v := range schemaRef.Value.Properties {
+                       if v.Value != nil {
+                               v.Value.XML = nil
+                               v.Value.Format = ""
+                       }
+                       if v.Ref != "" {
+                               schemaRef.Value.Properties[k] = 
getParameters(v.Ref, components)
+                       } else if v.Value.Items != nil && v.Value.Items.Ref != 
"" {
+                               v.Value.Items = 
getParameters(v.Value.Items.Ref, components)
+                       } else if v.Value.Items != nil && v.Value.Items.Value 
!= nil {
+                               v.Value.Items.Value.XML = nil
+                               v.Value.Items.Value.Format = ""
+                       }
+               }
+       }
+       return schemaRef
+}
diff --git a/api/internal/handler/data_loader/route_import_test.go 
b/api/internal/handler/data_loader/route_import_test.go
new file mode 100644
index 0000000..50f76f7
--- /dev/null
+++ b/api/internal/handler/data_loader/route_import_test.go
@@ -0,0 +1,178 @@
+/*
+ * 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 (
+       "bytes"
+       "errors"
+       "github.com/shiningrush/droplet/data"
+       "io/ioutil"
+       "mime/multipart"
+       "net/http"
+       "os/exec"
+       "strings"
+       "testing"
+
+       "github.com/shiningrush/droplet"
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/mock"
+
+       "github.com/apisix/manager-api/internal/core/store"
+)
+
+type testFile struct {
+       FieldName string
+       FileName  string
+       Content   []byte
+}
+
+func createRequestMultipartFiles(t *testing.T, files ...testFile) 
*http.Request {
+       var body bytes.Buffer
+
+       mw := multipart.NewWriter(&body)
+       for _, file := range files {
+               fw, err := mw.CreateFormFile(file.FieldName, file.FileName)
+               assert.NoError(t, err)
+
+               n, err := fw.Write(file.Content)
+               assert.NoError(t, err)
+               assert.Equal(t, len(file.Content), n)
+       }
+       err := mw.Close()
+       assert.NoError(t, err)
+
+       req, err := http.NewRequest("POST", "/", &body)
+       assert.NoError(t, err)
+
+       req.Header.Set("Content-Type", "multipart/form-data; 
boundary="+mw.Boundary())
+       return req
+}
+
+func TestImport_invalid_file_type(t *testing.T) {
+       input := &ImportInput{}
+       input.FileName = "file1.txt"
+       input.FileContent = []byte("hello")
+
+       h := ImportHandler{}
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       _, err := h.Import(ctx)
+       assert.EqualError(t, err, "required file type is .yaml, .yml or .json 
but got: .txt")
+}
+
+func TestImport_invalid_content(t *testing.T) {
+       input := &ImportInput{}
+       input.FileName = "file1.json"
+       input.FileContent = []byte(`{"test": "a"}`)
+
+       h := ImportHandler{}
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       _, err := h.Import(ctx)
+       assert.EqualError(t, err, "empty or invalid imported file")
+}
+
+func ReadFile(t *testing.T, filePath string) []byte {
+       cmd := exec.Command("pwd")
+       pwdByte, err := cmd.CombinedOutput()
+       pwd := string(pwdByte)
+       pwd = strings.Replace(pwd, "\n", "", 1)
+       dir := pwd[:strings.Index(pwd, "/api/")] + "/api/"
+       bytes, err := ioutil.ReadFile(dir + filePath)
+       assert.Nil(t, err)
+
+       return bytes
+}
+
+func TestImport_with_service_id(t *testing.T) {
+       bytes := ReadFile(t, "test/testdata/import/with-service-id.yaml")
+       input := &ImportInput{}
+       input.FileName = "file1.json"
+       input.FileContent = bytes
+
+       mStore := &store.MockInterface{}
+       mStore.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(nil, errors.New("data not found by key: service1"))
+
+       h := ImportHandler{
+               routeStore:    &store.GenericStore{},
+               svcStore:      mStore,
+               upstreamStore: mStore,
+       }
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       _, err := h.Import(ctx)
+       assert.EqualError(t, err, "data not found by key: service1")
+
+       //
+       mStore = &store.MockInterface{}
+       mStore.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(nil, data.ErrNotFound)
+
+       h = ImportHandler{
+               routeStore:    &store.GenericStore{},
+               svcStore:      mStore,
+               upstreamStore: mStore,
+       }
+       ctx = droplet.NewContext()
+       ctx.SetInput(input)
+
+       _, err = h.Import(ctx)
+       assert.EqualError(t, err, "service id: service1 not found")
+}
+
+func TestImport_with_upstream_id(t *testing.T) {
+       bytes := ReadFile(t, "test/testdata/import/with-upstream-id.yaml")
+       input := &ImportInput{}
+       input.FileName = "file1.json"
+       input.FileContent = bytes
+
+       mStore := &store.MockInterface{}
+       mStore.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(nil, errors.New("data not found by key: upstream1"))
+
+       h := ImportHandler{
+               routeStore:    &store.GenericStore{},
+               svcStore:      mStore,
+               upstreamStore: mStore,
+       }
+       ctx := droplet.NewContext()
+       ctx.SetInput(input)
+
+       _, err := h.Import(ctx)
+       assert.EqualError(t, err, "data not found by key: upstream1")
+
+       //
+       mStore = &store.MockInterface{}
+       mStore.On("Get", mock.Anything).Run(func(args mock.Arguments) {
+       }).Return(nil, data.ErrNotFound)
+
+       h = ImportHandler{
+               routeStore:    &store.GenericStore{},
+               svcStore:      mStore,
+               upstreamStore: mStore,
+       }
+       ctx = droplet.NewContext()
+       ctx.SetInput(input)
+
+       _, err = h.Import(ctx)
+       assert.EqualError(t, err, "upstream id: upstream1 not found")
+
+}
diff --git a/api/internal/handler/handler_test.go 
b/api/internal/handler/handler_test.go
index 7fd288c..e095bb6 100644
--- a/api/internal/handler/handler_test.go
+++ b/api/internal/handler/handler_test.go
@@ -21,8 +21,8 @@ import (
        "net/http"
        "testing"
 
-       "github.com/go-playground/assert/v2"
        "github.com/shiningrush/droplet/data"
+       "github.com/stretchr/testify/assert"
 )
 
 func TestSpecCodeResponse(t *testing.T) {
diff --git a/api/internal/route.go b/api/internal/route.go
index c1ace7e..ac78a36 100644
--- a/api/internal/route.go
+++ b/api/internal/route.go
@@ -75,6 +75,7 @@ func SetUpRouter() *gin.Engine {
                server_info.NewHandler,
                label.NewHandler,
                data_loader.NewHandler,
+               data_loader.NewImportHandler,
        }
 
        for i := range factories {
diff --git a/api/internal/utils/utils.go b/api/internal/utils/utils.go
index 0a612c2..3661bcb 100644
--- a/api/internal/utils/utils.go
+++ b/api/internal/utils/utils.go
@@ -17,11 +17,13 @@
 package utils
 
 import (
+       "bytes"
        "encoding/json"
        "errors"
        "fmt"
        "net"
        "os"
+       "sort"
        "strconv"
        "strings"
 
@@ -169,3 +171,38 @@ func ValidateLuaCode(code string) error {
        _, err := parse.Parse(strings.NewReader(code), "<string>")
        return err
 }
+
+//
+func StringSliceEqual(a, b []string) bool {
+       if (a == nil) != (b == nil) {
+               return false
+       }
+
+       if len(a) != len(b) {
+               return false
+       }
+
+       sort.Strings(a)
+       sort.Strings(b)
+
+       for i := range a {
+               if a[i] != b[i] {
+                       return false
+               }
+       }
+
+       return true
+}
+
+// value compare
+func ValueEqual(a interface{}, b interface{}) bool {
+       aBytes, err := json.Marshal(a)
+       if err != nil {
+               return false
+       }
+       bBytes, err := json.Marshal(b)
+       if err != nil {
+               return false
+       }
+       return bytes.Equal(aBytes, bBytes)
+}
diff --git a/api/test/e2e/base.go b/api/test/e2e/base.go
index 1cf59eb..0d9a333 100644
--- a/api/test/e2e/base.go
+++ b/api/test/e2e/base.go
@@ -77,11 +77,14 @@ func init() {
        Token = token
 }
 
-func httpGet(url string) ([]byte, int, error) {
+func httpGet(url string, headers map[string]string) ([]byte, int, error) {
        req, err := http.NewRequest(http.MethodGet, url, nil)
        if err != nil {
                return nil, 0, err
        }
+       for key, val := range headers {
+               req.Header.Add(key, val)
+       }
 
        client := &http.Client{}
        resp, err := client.Do(req)
diff --git a/api/test/e2e/http.go b/api/test/e2e/http.go
new file mode 100644
index 0000000..d6f1b6a
--- /dev/null
+++ b/api/test/e2e/http.go
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package e2e
+
+import (
+       "bytes"
+       "encoding/json"
+       "io"
+       "io/ioutil"
+       "mime/multipart"
+       "net/http"
+       "net/url"
+       "os"
+       "path/filepath"
+       "strings"
+)
+
+type UploadFile struct {
+       Name     string
+       Filepath string
+}
+
+var httpClient = &http.Client{}
+
+func PostFile(reqUrl string, reqParams map[string]string, files []UploadFile, 
headers map[string]string) ([]byte, int, error) {
+       return post(reqUrl, reqParams, "multipart/form-data", files, headers)
+}
+
+func post(reqUrl string, reqParams map[string]string, contentType string, 
files []UploadFile, headers map[string]string) ([]byte, int, error) {
+       requestBody, realContentType := getReader(reqParams, contentType, files)
+       httpRequest, _ := http.NewRequest("POST", reqUrl, requestBody)
+       httpRequest.Header.Add("Content-Type", realContentType)
+       if headers != nil {
+               for k, v := range headers {
+                       httpRequest.Header.Add(k, v)
+               }
+       }
+       resp, err := httpClient.Do(httpRequest)
+       if err != nil {
+               panic(err)
+       }
+
+       defer resp.Body.Close()
+
+       body, err := ioutil.ReadAll(resp.Body)
+       if err != nil {
+               return nil, 0, err
+       }
+
+       return body, resp.StatusCode, nil
+}
+
+func getReader(reqParams map[string]string, contentType string, files 
[]UploadFile) (io.Reader, string) {
+       if strings.Index(contentType, "json") > -1 {
+               bytesData, _ := json.Marshal(reqParams)
+               return bytes.NewReader(bytesData), contentType
+       }
+       if files != nil {
+               body := &bytes.Buffer{}
+               writer := multipart.NewWriter(body)
+               for _, uploadFile := range files {
+                       file, err := os.Open(uploadFile.Filepath)
+                       if err != nil {
+                               panic(err)
+                       }
+                       part, err := writer.CreateFormFile(uploadFile.Name, 
filepath.Base(uploadFile.Filepath))
+                       if err != nil {
+                               panic(err)
+                       }
+                       _, err = io.Copy(part, file)
+                       file.Close()
+               }
+               for k, v := range reqParams {
+                       if err := writer.WriteField(k, v); err != nil {
+                               panic(err)
+                       }
+               }
+               if err := writer.Close(); err != nil {
+                       panic(err)
+               }
+               return body, writer.FormDataContentType()
+       }
+
+       urlValues := url.Values{}
+       for key, val := range reqParams {
+               urlValues.Set(key, val)
+       }
+
+       reqBody := urlValues.Encode()
+
+       return strings.NewReader(reqBody), contentType
+}
diff --git a/api/test/e2e/route_export_test.go 
b/api/test/e2e/route_export_test.go
index 5871d76..7c9b5c6 100644
--- a/api/test/e2e/route_export_test.go
+++ b/api/test/e2e/route_export_test.go
@@ -1463,13 +1463,13 @@ func TestExportRoute_With_Jwt_Plugin(t *testing.T) {
        time.Sleep(sleepTime)
 
        // sign jwt token
-       body, status, err := 
httpGet("http://127.0.0.10:9080/apisix/plugin/jwt/sign?key=user-key";)
+       body, status, err := 
httpGet("http://127.0.0.10:9080/apisix/plugin/jwt/sign?key=user-key";, nil)
        assert.Nil(t, err)
        assert.Equal(t, http.StatusOK, status)
        jwtToken := string(body)
 
        // sign jwt token with not exists key
-       body, status, err = 
httpGet("http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=not-exist-key";)
+       body, status, err = 
httpGet("http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=not-exist-key";, nil)
        assert.Nil(t, err)
        assert.Equal(t, http.StatusNotFound, status)
 
@@ -2485,4 +2485,3 @@ func replaceStr(str string) string {
        str = strings.Replace(str, " ", "", -1)
        return str
 }
-
diff --git a/api/test/e2e/route_import_test.go 
b/api/test/e2e/route_import_test.go
new file mode 100644
index 0000000..62a6c97
--- /dev/null
+++ b/api/test/e2e/route_import_test.go
@@ -0,0 +1,579 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package e2e
+
+import (
+       "io/ioutil"
+       "net/http"
+       "path/filepath"
+       "strings"
+       "testing"
+       "time"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/tidwall/gjson"
+)
+
+func TestImport_default(t *testing.T) {
+       path, err := filepath.Abs("../testdata/import/default.yaml")
+       assert.Nil(t, err)
+
+       headers := map[string]string{
+               "Authorization": token,
+       }
+       files := []UploadFile{
+               {Name: "file", Filepath: path},
+       }
+       PostFile(ManagerAPIHost+"/apisix/admin/import/routes", nil, files, 
headers)
+
+       // sleep for data sync
+       time.Sleep(sleepTime)
+
+       request, _ := http.NewRequest("GET", 
ManagerAPIHost+"/apisix/admin/routes", nil)
+       request.Header.Add("Authorization", token)
+       resp, err := http.DefaultClient.Do(request)
+       assert.Nil(t, err)
+       defer resp.Body.Close()
+       respBody, _ := ioutil.ReadAll(resp.Body)
+       list := gjson.Get(string(respBody), "data.rows").Value().([]interface{})
+
+       var tests []HttpTestCase
+       for _, item := range list {
+               route := item.(map[string]interface{})
+               tc := HttpTestCase{
+                       Desc:         "route patch for update status(online)",
+                       Object:       ManagerApiExpect(t),
+                       Method:       http.MethodPatch,
+                       Path:         "/apisix/admin/routes/" + 
route["id"].(string),
+                       Body:         `{"status":1}`,
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+                       Sleep:        sleepTime,
+               }
+               tests = append(tests, tc)
+       }
+
+       // verify route
+       tests = append(tests, HttpTestCase{
+               Desc:         "verify the route just imported",
+               Object:       APISIXExpect(t),
+               Method:       http.MethodGet,
+               Path:         "/hello",
+               ExpectStatus: http.StatusOK,
+               ExpectBody:   "hello world",
+               Sleep:        sleepTime,
+       })
+
+       // delete test data
+       for _, item := range list {
+               route := item.(map[string]interface{})
+               tc := HttpTestCase{
+                       Desc:         "delete route",
+                       Object:       ManagerApiExpect(t),
+                       Method:       http.MethodDelete,
+                       Path:         "/apisix/admin/routes/" + 
route["id"].(string),
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+               }
+               tests = append(tests, tc)
+       }
+
+       for _, tc := range tests {
+               testCaseCheck(tc, t)
+       }
+}
+
+func TestImport_json(t *testing.T) {
+       path, err := filepath.Abs("../testdata/import/default.json")
+       assert.Nil(t, err)
+
+       headers := map[string]string{
+               "Authorization": token,
+       }
+       files := []UploadFile{
+               {Name: "file", Filepath: path},
+       }
+       PostFile(ManagerAPIHost+"/apisix/admin/import/routes", nil, files, 
headers)
+
+       // sleep for data sync
+       time.Sleep(sleepTime)
+
+       request, _ := http.NewRequest("GET", 
ManagerAPIHost+"/apisix/admin/routes", nil)
+       request.Header.Add("Authorization", token)
+       resp, err := http.DefaultClient.Do(request)
+       assert.Nil(t, err)
+       defer resp.Body.Close()
+       respBody, _ := ioutil.ReadAll(resp.Body)
+       list := gjson.Get(string(respBody), "data.rows").Value().([]interface{})
+
+       var tests []HttpTestCase
+       for _, item := range list {
+               route := item.(map[string]interface{})
+               tc := HttpTestCase{
+                       Desc:         "route patch for update status(online)",
+                       Object:       ManagerApiExpect(t),
+                       Method:       http.MethodPatch,
+                       Path:         "/apisix/admin/routes/" + 
route["id"].(string),
+                       Body:         `{"status":1}`,
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+                       Sleep:        sleepTime,
+               }
+               tests = append(tests, tc)
+       }
+
+       // verify route
+       tests = append(tests, HttpTestCase{
+               Desc:         "verify the route just imported",
+               Object:       APISIXExpect(t),
+               Method:       http.MethodGet,
+               Path:         "/hello",
+               ExpectStatus: http.StatusOK,
+               ExpectBody:   "hello world",
+               Sleep:        sleepTime,
+       })
+
+       // delete test data
+       for _, item := range list {
+               route := item.(map[string]interface{})
+               tc := HttpTestCase{
+                       Desc:         "delete route",
+                       Object:       ManagerApiExpect(t),
+                       Method:       http.MethodDelete,
+                       Path:         "/apisix/admin/routes/" + 
route["id"].(string),
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+               }
+               tests = append(tests, tc)
+       }
+
+       for _, tc := range tests {
+               testCaseCheck(tc, t)
+       }
+}
+
+func TestImport_with_plugins(t *testing.T) {
+       path, err := filepath.Abs("../testdata/import/with-plugins.yaml")
+       assert.Nil(t, err)
+
+       headers := map[string]string{
+               "Authorization": token,
+       }
+       files := []UploadFile{
+               {Name: "file", Filepath: path},
+       }
+       PostFile(ManagerAPIHost+"/apisix/admin/import/routes", nil, files, 
headers)
+
+       // sleep for data sync
+       time.Sleep(sleepTime)
+
+       request, _ := http.NewRequest("GET", 
ManagerAPIHost+"/apisix/admin/routes", nil)
+       request.Header.Add("Authorization", token)
+       resp, err := http.DefaultClient.Do(request)
+       assert.Nil(t, err)
+       defer resp.Body.Close()
+       respBody, _ := ioutil.ReadAll(resp.Body)
+       list := gjson.Get(string(respBody), "data.rows").Value().([]interface{})
+
+       var tests []HttpTestCase
+       for _, item := range list {
+               route := item.(map[string]interface{})
+               tc := HttpTestCase{
+                       Desc:         "route patch for update status(online)",
+                       Object:       ManagerApiExpect(t),
+                       Method:       http.MethodPatch,
+                       Path:         "/apisix/admin/routes/" + 
route["id"].(string),
+                       Body:         `{"status":1}`,
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+                       Sleep:        sleepTime,
+               }
+               tests = append(tests, tc)
+       }
+
+       // verify route
+       verifyTests := []HttpTestCase{
+               {
+                       Desc:         "verify the route just imported",
+                       Object:       APISIXExpect(t),
+                       Method:       http.MethodPost,
+                       Path:         "/hello",
+                       Body:         `{}`,
+                       ExpectStatus: http.StatusBadRequest,
+                       ExpectBody:   `property "id" is required`,
+                       Sleep:        sleepTime,
+               },
+               {
+                       Desc:         "verify the route just imported",
+                       Object:       APISIXExpect(t),
+                       Method:       http.MethodPost,
+                       Path:         "/hello",
+                       Headers:      map[string]string{"id": "1"},
+                       Body:         `{}`,
+                       ExpectStatus: http.StatusBadRequest,
+                       ExpectBody:   `property "status" is required`,
+                       Sleep:        sleepTime,
+               },
+               {
+                       Desc:         "verify the route just imported",
+                       Object:       APISIXExpect(t),
+                       Method:       http.MethodPost,
+                       Path:         "/hello",
+                       Headers:      map[string]string{"id": "1"},
+                       Body:         `{"status": "1"}`,
+                       ExpectStatus: http.StatusUnauthorized,
+                       ExpectBody:   `{"message":"Missing authorization in 
request"}`,
+                       Sleep:        sleepTime,
+               },
+       }
+       tests = append(tests, verifyTests...)
+
+       // delete test data
+       for _, item := range list {
+               route := item.(map[string]interface{})
+               tc := HttpTestCase{
+                       Desc:         "delete route",
+                       Object:       ManagerApiExpect(t),
+                       Method:       http.MethodDelete,
+                       Path:         "/apisix/admin/routes/" + 
route["id"].(string),
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+               }
+               tests = append(tests, tc)
+       }
+
+       for _, tc := range tests {
+               testCaseCheck(tc, t)
+       }
+}
+
+func TestImport_with_multi_routes(t *testing.T) {
+       path, err := filepath.Abs("../testdata/import/multi-routes.yaml")
+       assert.Nil(t, err)
+
+       headers := map[string]string{
+               "Authorization": token,
+       }
+       files := []UploadFile{
+               {Name: "file", Filepath: path},
+       }
+       PostFile(ManagerAPIHost+"/apisix/admin/import/routes", nil, files, 
headers)
+
+       // sleep for data sync
+       time.Sleep(sleepTime)
+
+       request, _ := http.NewRequest("GET", 
ManagerAPIHost+"/apisix/admin/routes", nil)
+       request.Header.Add("Authorization", token)
+       resp, err := http.DefaultClient.Do(request)
+       assert.Nil(t, err)
+       defer resp.Body.Close()
+       respBody, _ := ioutil.ReadAll(resp.Body)
+       list := gjson.Get(string(respBody), "data.rows").Value().([]interface{})
+
+       assert.Equal(t, 2, len(list))
+
+       var tests []HttpTestCase
+       for _, item := range list {
+               route := item.(map[string]interface{})
+               tc := HttpTestCase{
+                       Desc:         "route patch for update status(online)",
+                       Object:       ManagerApiExpect(t),
+                       Method:       http.MethodPatch,
+                       Path:         "/apisix/admin/routes/" + 
route["id"].(string),
+                       Body:         `{"status":1}`,
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+                       Sleep:        sleepTime,
+               }
+               tests = append(tests, tc)
+               uris := route["uris"].([]interface{})
+               isGet := false
+               for _, uri := range uris {
+                       if uri == "/get" {
+                               isGet = true
+                       }
+               }
+               // verify route data
+               if isGet {
+                       tcDataVerify := HttpTestCase{
+                               Desc:         "verify data of route",
+                               Object:       ManagerApiExpect(t),
+                               Method:       http.MethodGet,
+                               Path:         "/apisix/admin/routes/" + 
route["id"].(string),
+                               Headers:      
map[string]string{"Authorization": token},
+                               ExpectStatus: http.StatusOK,
+                               ExpectBody: 
[]string{`"methods":["GET","POST","HEAD","PUT","PATCH","DELETE"]`,
+                                       
`"proxy-rewrite":{"disable":false,"scheme":"https"}`,
+                                       
`"labels":{"API_VERSION":"v2","dev":"test"}`,
+                                       
`"upstream":{"nodes":[{"host":"httpbin.org","port":443,"weight":1}],"timeout":{"connect":6000,"read":6000,"send":6000},"type":"roundrobin","pass_host":"node"}`,
+                               },
+                               Sleep: sleepTime,
+                       }
+                       tests = append(tests, tcDataVerify)
+               } else {
+                       tcDataVerify := HttpTestCase{
+                               Desc:         "verify data of route2",
+                               Object:       ManagerApiExpect(t),
+                               Method:       http.MethodGet,
+                               Path:         "/apisix/admin/routes/" + 
route["id"].(string),
+                               Headers:      
map[string]string{"Authorization": token},
+                               ExpectStatus: http.StatusOK,
+                               ExpectBody: []string{`"methods":["POST"]`,
+                                       
`"proxy-rewrite":{"disable":false,"scheme":"https"}`,
+                                       
`"labels":{"API_VERSION":"v1","version":"v1"}`,
+                                       
`"upstream":{"nodes":[{"host":"httpbin.org","port":443,"weight":1}],"timeout":{"connect":6000,"read":6000,"send":6000},"type":"roundrobin","pass_host":"node"}`,
+                               },
+                               Sleep: sleepTime,
+                       }
+                       tests = append(tests, tcDataVerify)
+               }
+       }
+
+       // verify route
+       verifyTests := []HttpTestCase{
+               {
+                       Desc:         "verify the route just imported",
+                       Object:       APISIXExpect(t),
+                       Method:       http.MethodGet,
+                       Path:         "/get",
+                       ExpectStatus: http.StatusOK,
+                       ExpectBody:   `"url": "https://127.0.0.1/get"`,
+                       Sleep:        sleepTime,
+               },
+               {
+                       Desc:         "verify the route just imported",
+                       Object:       APISIXExpect(t),
+                       Method:       http.MethodPost,
+                       Path:         "/post",
+                       ExpectStatus: http.StatusOK,
+                       ExpectBody:   `"url": "https://127.0.0.1/post"`,
+                       Sleep:        sleepTime,
+               },
+       }
+       tests = append(tests, verifyTests...)
+
+       // delete test data
+       for _, item := range list {
+               route := item.(map[string]interface{})
+               tc := HttpTestCase{
+                       Desc:         "delete route",
+                       Object:       ManagerApiExpect(t),
+                       Method:       http.MethodDelete,
+                       Path:         "/apisix/admin/routes/" + 
route["id"].(string),
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+               }
+               tests = append(tests, tc)
+       }
+
+       for _, tc := range tests {
+               testCaseCheck(tc, t)
+       }
+}
+
+func TestRoute_export_import(t *testing.T) {
+       // create routes
+       tests := []HttpTestCase{
+               {
+                       Desc:   "Create a route",
+                       Object: ManagerApiExpect(t),
+                       Method: http.MethodPut,
+                       Path:   "/apisix/admin/routes/r1",
+                       Body: `{
+                                       "uris": ["/test-test1"],
+                                       "name": "route_all",
+                                       "desc": "所有",
+                                       "methods": ["GET"],
+                                       "hosts": ["test.com"],
+                                       "status": 1,
+                                       "upstream": {
+                                               "nodes": {
+                                                       "172.16.238.20:1980": 1
+                                               },
+                                               "type": "roundrobin"
+                                       }
+                       }`,
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+                       Sleep:        sleepTime,
+               },
+               {
+                       Desc:   "Create a route2",
+                       Object: ManagerApiExpect(t),
+                       Method: http.MethodPut,
+                       Path:   "/apisix/admin/routes/r2",
+                       Body: `{
+                                       "uris": ["/test-test2"],
+                                       "name": "route_all",
+                                       "desc": "所有1",
+                                       "methods": ["GET"],
+                                       "hosts": ["test.com"],
+                                       "status": 1,
+                                       "upstream": {
+                                               "nodes": {
+                                                       "172.16.238.20:1980": 1
+                                               },
+                                               "type": "roundrobin"
+                                       }
+                       }`,
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+                       Sleep:        sleepTime,
+               },
+               {
+                       Desc:   "Create a route3",
+                       Object: ManagerApiExpect(t),
+                       Method: http.MethodPut,
+                       Path:   "/apisix/admin/routes/r3",
+                       Body: `{
+                                       "uris": ["/test-test3"],
+                                       "name": "route_all",
+                                       "desc": "所有2",
+                                       "methods": ["GET"],
+                                       "hosts": ["test.com"],
+                                       "status": 1,
+                                       "upstream": {
+                                               "nodes": {
+                                                       "172.16.238.20:1980": 1
+                                               },
+                                               "type": "roundrobin"
+                                       }
+                       }`,
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+                       Sleep:        sleepTime,
+               },
+       }
+       for _, tc := range tests {
+               testCaseCheck(tc, t)
+       }
+
+       // export routes
+       time.Sleep(sleepTime)
+       tmpPath := "/tmp/export.json"
+       headers := map[string]string{
+               "Authorization": token,
+       }
+       body, status, err := 
httpGet(ManagerAPIHost+"/apisix/admin/export/routes", headers)
+       assert.Nil(t, err)
+       assert.Equal(t, http.StatusOK, status)
+
+       content := gjson.Get(string(body), "data")
+       err = ioutil.WriteFile(tmpPath, []byte(content.Raw), 0644)
+       assert.Nil(t, err)
+
+       // import routes (should failed -- duplicate)
+       files := []UploadFile{
+               {Name: "file", Filepath: tmpPath},
+       }
+       respBody, status, err := 
PostFile(ManagerAPIHost+"/apisix/admin/import/routes", nil, files, headers)
+       assert.Nil(t, err)
+       assert.Equal(t, 400, status)
+       assert.True(t, strings.Contains(string(respBody), "duplicate"))
+       time.Sleep(sleepTime)
+
+       // delete routes
+       tests = []HttpTestCase{
+               {
+                       Desc:         "delete the route1 just created",
+                       Object:       ManagerApiExpect(t),
+                       Method:       http.MethodDelete,
+                       Path:         "/apisix/admin/routes/r1",
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+               },
+               {
+                       Desc:         "delete the route2 just created",
+                       Object:       ManagerApiExpect(t),
+                       Method:       http.MethodDelete,
+                       Path:         "/apisix/admin/routes/r2",
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+               },
+               {
+                       Desc:         "delete the route3 just created",
+                       Object:       ManagerApiExpect(t),
+                       Method:       http.MethodDelete,
+                       Path:         "/apisix/admin/routes/r3",
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+               },
+       }
+       for _, tc := range tests {
+               testCaseCheck(tc, t)
+       }
+
+       // import again
+       time.Sleep(sleepTime)
+       respBody, status, err = 
PostFile(ManagerAPIHost+"/apisix/admin/import/routes", nil, files, headers)
+       assert.Nil(t, err)
+       assert.Equal(t, 200, status)
+       assert.True(t, strings.Contains(string(respBody), 
`"data":{"paths":3,"routes":3}`))
+       time.Sleep(sleepTime)
+
+       // sleep for data sync
+       time.Sleep(sleepTime)
+
+       request, _ := http.NewRequest("GET", 
ManagerAPIHost+"/apisix/admin/routes", nil)
+       request.Header.Add("Authorization", token)
+       resp, err := http.DefaultClient.Do(request)
+       assert.Nil(t, err)
+       defer resp.Body.Close()
+       respBody, _ = ioutil.ReadAll(resp.Body)
+       list := gjson.Get(string(respBody), "data.rows").Value().([]interface{})
+
+       assert.Equal(t, 3, len(list))
+
+       // verify route data
+       tests = []HttpTestCase{}
+       for _, item := range list {
+               route := item.(map[string]interface{})
+               tcDataVerify := HttpTestCase{
+                       Desc:         "verify data of route2",
+                       Object:       ManagerApiExpect(t),
+                       Method:       http.MethodGet,
+                       Path:         "/apisix/admin/routes/" + 
route["id"].(string),
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+                       ExpectBody: []string{`"methods":["GET"]`,
+                               `"desc":"所有`,
+                               `"hosts":["test.com"]`,
+                               
`"upstream":{"nodes":[{"host":"172.16.238.20","port":1980,"weight":1}],"type":"roundrobin"}`,
+                       },
+                       Sleep: sleepTime,
+               }
+               tests = append(tests, tcDataVerify)
+       }
+
+       // delete test data
+       for _, item := range list {
+               route := item.(map[string]interface{})
+               tc := HttpTestCase{
+                       Desc:         "delete route",
+                       Object:       ManagerApiExpect(t),
+                       Method:       http.MethodDelete,
+                       Path:         "/apisix/admin/routes/" + 
route["id"].(string),
+                       Headers:      map[string]string{"Authorization": token},
+                       ExpectStatus: http.StatusOK,
+               }
+               tests = append(tests, tc)
+       }
+
+       for _, tc := range tests {
+               testCaseCheck(tc, t)
+       }
+}
diff --git a/api/test/e2e/route_online_debug_test.go 
b/api/test/e2e/route_online_debug_test.go
index fba4f03..4665309 100644
--- a/api/test/e2e/route_online_debug_test.go
+++ b/api/test/e2e/route_online_debug_test.go
@@ -485,7 +485,7 @@ func TestRoute_Online_Debug_Route_With_Jwt_Auth(t 
*testing.T) {
        time.Sleep(sleepTime)
 
        // sign jwt token
-       body, status, err := 
httpGet("http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=user-key";)
+       body, status, err := 
httpGet("http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=user-key";, nil)
        assert.Nil(t, err)
        assert.Equal(t, http.StatusOK, status)
        jwtToken := string(body)
diff --git a/api/test/e2e/route_with_plugin_jwt_test.go 
b/api/test/e2e/route_with_plugin_jwt_test.go
index 9ceec91..3bf50f1 100644
--- a/api/test/e2e/route_with_plugin_jwt_test.go
+++ b/api/test/e2e/route_with_plugin_jwt_test.go
@@ -94,13 +94,13 @@ func TestRoute_With_Jwt_Plugin(t *testing.T) {
        time.Sleep(sleepTime)
 
        // sign jwt token
-       body, status, err := 
httpGet("http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=user-key";)
+       body, status, err := 
httpGet("http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=user-key";, nil)
        assert.Nil(t, err)
        assert.Equal(t, http.StatusOK, status)
        jwtToken := string(body)
 
        // sign jwt token with not exists key
-       body, status, err = 
httpGet("http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=not-exist-key";)
+       body, status, err = 
httpGet("http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=not-exist-key";, nil)
        assert.Nil(t, err)
        assert.Equal(t, http.StatusNotFound, status)
 
diff --git a/api/test/testdata/import/default.json 
b/api/test/testdata/import/default.json
new file mode 100644
index 0000000..0d20878
--- /dev/null
+++ b/api/test/testdata/import/default.json
@@ -0,0 +1,39 @@
+{
+  "openapi": "3.0.0",
+  "info": {
+    "version": "1.0.0-oas3",
+    "description": "test desc",
+    "license": {
+      "name": "Apache License 2.0",
+      "url": "http://www.apache.org/licenses/LICENSE-2.0";
+    },
+    "title": "test title"
+  },
+  "paths": {
+    "/hello": {
+      "get": {
+        "x-api-limit": 20,
+        "description": "hello world.",
+        "operationId": "hello",
+        "x-apisix-upstream": {
+          "type": "roundrobin",
+          "nodes": [
+            {
+              "host": "172.16.238.20",
+              "port": 1980,
+              "weight": 1
+            }
+          ]
+        },
+        "responses": {
+          "200": {
+            "description": "list response"
+          },
+          "default": {
+            "description": "unexpected error"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/api/test/testdata/import/default.yaml 
b/api/test/testdata/import/default.yaml
new file mode 100644
index 0000000..3f69887
--- /dev/null
+++ b/api/test/testdata/import/default.yaml
@@ -0,0 +1,44 @@
+#
+# 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.
+#
+# If you want to set the specified configuration value, you can set the new
+# in this file. For example if you want to specify the etcd address:
+#
+openapi: 3.0.0
+info:
+  version: 1.0.0-oas3
+  description: test desc
+  license:
+    name: Apache License 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0'
+  title: test title
+paths:
+  /hello:
+    get:
+      x-api-limit: 20
+      description: hello world.
+      operationId: hello
+      x-apisix-upstream:
+        type: roundrobin
+        nodes:
+          - host: 172.16.238.20
+            port: 1980
+            weight: 1
+      responses:
+        '200':
+          description: list response
+        default:
+          description: unexpected error
diff --git a/api/test/testdata/import/multi-routes.yaml 
b/api/test/testdata/import/multi-routes.yaml
new file mode 100644
index 0000000..966815f
--- /dev/null
+++ b/api/test/testdata/import/multi-routes.yaml
@@ -0,0 +1,224 @@
+#
+# 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.
+#
+# If you want to set the specified configuration value, you can set the new
+# in this file. For example if you want to specify the etcd address:
+#
+components: {}
+info:
+  title: RoutesExport
+  version: 3.0.0
+openapi: 3.0.0
+paths:
+  /get:
+    delete:
+      operationId: api1DELETE
+      requestBody: {}
+      responses:
+        default:
+          description: ''
+      x-apisix-enableWebsocket: false
+      x-apisix-labels:
+        API_VERSION: v2
+        dev: test
+      x-apisix-plugins:
+        proxy-rewrite:
+          disable: false
+          scheme: https
+      x-apisix-priority: 0
+      x-apisix-status: 1
+      x-apisix-upstream:
+        nodes:
+          - host: httpbin.org
+            port: 443
+            weight: 1
+        timeout:
+          connect: 6000
+          read: 6000
+          send: 6000
+        type: roundrobin
+        pass_host: node
+      x-apisix-vars: []
+    get:
+      operationId: api1GET
+      requestBody: {}
+      responses:
+        default:
+          description: ''
+      x-apisix-enableWebsocket: false
+      x-apisix-labels:
+        API_VERSION: v2
+        dev: test
+      x-apisix-plugins:
+        proxy-rewrite:
+          disable: false
+          scheme: https
+      x-apisix-priority: 0
+      x-apisix-status: 1
+      x-apisix-upstream:
+        nodes:
+          - host: httpbin.org
+            port: 443
+            weight: 1
+        timeout:
+          connect: 6000
+          read: 6000
+          send: 6000
+        type: roundrobin
+        pass_host: node
+      x-apisix-vars: []
+    head:
+      operationId: api1HEAD
+      requestBody: {}
+      responses:
+        default:
+          description: ''
+      x-apisix-enableWebsocket: false
+      x-apisix-labels:
+        API_VERSION: v2
+        dev: test
+      x-apisix-plugins:
+        proxy-rewrite:
+          disable: false
+          scheme: https
+      x-apisix-priority: 0
+      x-apisix-status: 1
+      x-apisix-upstream:
+        nodes:
+          - host: httpbin.org
+            port: 443
+            weight: 1
+        timeout:
+          connect: 6000
+          read: 6000
+          send: 6000
+        type: roundrobin
+        pass_host: node
+      x-apisix-vars: []
+    patch:
+      operationId: api1PATCH
+      requestBody: {}
+      responses:
+        default:
+          description: ''
+      x-apisix-enableWebsocket: false
+      x-apisix-labels:
+        API_VERSION: v2
+        dev: test
+      x-apisix-plugins:
+        proxy-rewrite:
+          disable: false
+          scheme: https
+      x-apisix-priority: 0
+      x-apisix-status: 1
+      x-apisix-upstream:
+        nodes:
+          - host: httpbin.org
+            port: 443
+            weight: 1
+        timeout:
+          connect: 6000
+          read: 6000
+          send: 6000
+        type: roundrobin
+        pass_host: node
+      x-apisix-vars: []
+    post:
+      operationId: api1POST
+      requestBody: {}
+      responses:
+        default:
+          description: ''
+      x-apisix-enableWebsocket: false
+      x-apisix-labels:
+        API_VERSION: v2
+        dev: test
+      x-apisix-plugins:
+        proxy-rewrite:
+          disable: false
+          scheme: https
+      x-apisix-priority: 0
+      x-apisix-status: 1
+      x-apisix-upstream:
+        nodes:
+          - host: httpbin.org
+            port: 443
+            weight: 1
+        timeout:
+          connect: 6000
+          read: 6000
+          send: 6000
+        type: roundrobin
+        pass_host: node
+      x-apisix-vars: []
+    put:
+      operationId: api1PUT
+      requestBody: {}
+      responses:
+        default:
+          description: ''
+      x-apisix-enableWebsocket: false
+      x-apisix-labels:
+        API_VERSION: v2
+        dev: test
+      x-apisix-plugins:
+        proxy-rewrite:
+          disable: false
+          scheme: https
+      x-apisix-priority: 0
+      x-apisix-status: 1
+      x-apisix-upstream:
+        nodes:
+          - host: httpbin.org
+            port: 443
+            weight: 1
+        timeout:
+          connect: 6000
+          read: 6000
+          send: 6000
+        type: roundrobin
+        pass_host: node
+      x-apisix-vars: []
+  /post:
+    post:
+      operationId: test_postPOST
+      requestBody: {}
+      responses:
+        default:
+          description: ''
+      security: []
+      x-apisix-enableWebsocket: false
+      x-apisix-labels:
+        API_VERSION: v1
+        version: v1
+      x-apisix-plugins:
+        proxy-rewrite:
+          disable: false
+          scheme: https
+      x-apisix-priority: 0
+      x-apisix-status: 1
+      x-apisix-upstream:
+        nodes:
+          - host: httpbin.org
+            port: 443
+            weight: 1
+        timeout:
+          connect: 6000
+          read: 6000
+          send: 6000
+        type: roundrobin
+        pass_host: node
+      x-apisix-vars: []
diff --git a/api/test/testdata/import/with-plugins.yaml 
b/api/test/testdata/import/with-plugins.yaml
new file mode 100644
index 0000000..68377d1
--- /dev/null
+++ b/api/test/testdata/import/with-plugins.yaml
@@ -0,0 +1,80 @@
+#
+# 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.
+#
+# If you want to set the specified configuration value, you can set the new
+# in this file. For example if you want to specify the etcd address:
+#
+
+components:
+  securitySchemes:
+    basicAuth:
+      type: http
+      scheme: basic
+info:
+  version: "1"
+  description: |-
+    test desc
+  license:
+    name: Apache License 2.0
+    url: http://www.apache.org/licenses/LICENSE-2.0
+  title: |-
+    test title
+paths:
+  /hello:
+    post:
+      x-api-limit: 20
+      description: |-
+        hello world.
+      operationId: hello
+      x-apisix-upstream:
+        type: roundrobin
+        nodes:
+          - host: "172.16.238.20"
+            port: 1980
+            weight: 1
+      parameters:
+        - name: id
+          in: header
+          description: ID of pet to use
+          required: true
+          schema:
+            type: string
+          style: simple
+
+      requestBody:
+        content:
+          'application/x-www-form-urlencoded':
+            schema:
+              properties:
+                name:
+                  description: Update pet's name
+                  type: string
+                status:
+                  description: Updated status of the pet
+                  type: string
+              required:
+                - status
+
+      security:
+        - basicAuth: []
+
+      responses:
+        200:
+          description: list response
+        default:
+          description: unexpected error
+
+openapi: 3.0.0
diff --git a/api/test/testdata/import/with-service-id.yaml 
b/api/test/testdata/import/with-service-id.yaml
new file mode 100644
index 0000000..985b02e
--- /dev/null
+++ b/api/test/testdata/import/with-service-id.yaml
@@ -0,0 +1,39 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# If you want to set the specified configuration value, you can set the new
+# in this file. For example if you want to specify the etcd address:
+#
+openapi: 3.0.0
+info:
+  version: 1.0.0-oas3
+  description: test desc
+  license:
+    name: Apache License 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0'
+  title: test title
+paths:
+  /hello:
+    get:
+      x-api-limit: 20
+      description: hello world.
+      operationId: hello
+      x-apisix-service_id: service1
+      responses:
+        '200':
+          description: list response
+        default:
+          description: unexpected error
diff --git a/api/test/testdata/import/with-upstream-id.yaml 
b/api/test/testdata/import/with-upstream-id.yaml
new file mode 100644
index 0000000..dc25276
--- /dev/null
+++ b/api/test/testdata/import/with-upstream-id.yaml
@@ -0,0 +1,39 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# If you want to set the specified configuration value, you can set the new
+# in this file. For example if you want to specify the etcd address:
+#
+openapi: 3.0.0
+info:
+  version: 1.0.0-oas3
+  description: test desc
+  license:
+    name: Apache License 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0'
+  title: test title
+paths:
+  /hello:
+    get:
+      x-api-limit: 20
+      description: hello world.
+      operationId: hello
+      x-apisix-upstream_id: upstream1
+      responses:
+        '200':
+          description: list response
+        default:
+          description: unexpected error

Reply via email to