You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by bz...@apache.org on 2021/10/19 05:32:18 UTC
[apisix-dashboard] branch master updated: feat: support proto API
(#2099)
This is an automated email from the ASF dual-hosted git repository.
bzp2010 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 9f17637 feat: support proto API (#2099)
9f17637 is described below
commit 9f17637090627e971d244be33c72629f6dd051a3
Author: bzp2010 <bz...@apache.org>
AuthorDate: Tue Oct 19 00:32:13 2021 -0500
feat: support proto API (#2099)
---
.github/workflows/backend-e2e-test.yml | 14 ++
api/internal/handler/proto/proto.go | 260 ++++++++++++++++++++++++++++++
api/internal/handler/proto/proto_test.go | 47 ++++++
api/internal/route.go | 2 +
api/test/docker/docker-compose.yaml | 10 ++
api/test/e2enew/base/base.go | 1 +
api/test/e2enew/proto/proto_suite_test.go | 40 +++++
api/test/e2enew/proto/proto_test.go | 255 +++++++++++++++++++++++++++++
8 files changed, 629 insertions(+)
diff --git a/.github/workflows/backend-e2e-test.yml b/.github/workflows/backend-e2e-test.yml
index ba8d43d..f870cfa 100644
--- a/.github/workflows/backend-e2e-test.yml
+++ b/.github/workflows/backend-e2e-test.yml
@@ -70,6 +70,13 @@ jobs:
--set *.cache-from=type=local,src=/tmp/.buildx-cache \
--set *.cache-to=type=local,dest=/tmp/.buildx-cache
+ - name: build and start grpc_server_example
+ working-directory: ./api/test/docker
+ run: |
+ wget https://github.com/api7/grpc_server_example/archive/refs/tags/20210819.tar.gz
+ tar -xzvf 20210819.tar.gz && cd grpc_server_example-20210819
+ docker build -t grpc_server_example:latest .
+
- name: run docker compose
working-directory: ./api/test/docker
run: |
@@ -171,6 +178,13 @@ jobs:
--set *.cache-from=type=local,src=/tmp/.buildx-cache \
--set *.cache-to=type=local,dest=/tmp/.buildx-cache
+ - name: build and start grpc_server_example
+ working-directory: ./api/test/docker
+ run: |
+ wget https://github.com/api7/grpc_server_example/archive/refs/tags/20210819.tar.gz
+ tar -xzvf 20210819.tar.gz && cd grpc_server_example-20210819
+ docker build -t grpc_server_example:latest .
+
- name: run docker compose
working-directory: ./api/test/docker
run: |
diff --git a/api/internal/handler/proto/proto.go b/api/internal/handler/proto/proto.go
new file mode 100644
index 0000000..6c815e7
--- /dev/null
+++ b/api/internal/handler/proto/proto.go
@@ -0,0 +1,260 @@
+/*
+ * 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 proto
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "net/http"
+ "reflect"
+ "strings"
+
+ "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/core/entity"
+ "github.com/apisix/manager-api/internal/core/store"
+ "github.com/apisix/manager-api/internal/handler"
+ "github.com/apisix/manager-api/internal/utils"
+)
+
+type Handler struct {
+ routeStore store.Interface
+ serviceStore store.Interface
+ consumerStore store.Interface
+ pluginConfigStore store.Interface
+ globalRuleStore store.Interface
+ protoStore store.Interface
+}
+
+func NewHandler() (handler.RouteRegister, error) {
+ return &Handler{
+ routeStore: store.GetStore(store.HubKeyRoute),
+ serviceStore: store.GetStore(store.HubKeyService),
+ consumerStore: store.GetStore(store.HubKeyConsumer),
+ pluginConfigStore: store.GetStore(store.HubKeyPluginConfig),
+ globalRuleStore: store.GetStore(store.HubKeyGlobalRule),
+ protoStore: store.GetStore(store.HubKeyProto),
+ }, nil
+}
+
+func (h *Handler) ApplyRoute(r *gin.Engine) {
+ r.GET("/apisix/admin/proto/:id", wgin.Wraps(h.Get,
+ wrapper.InputType(reflect.TypeOf(GetInput{}))))
+ r.GET("/apisix/admin/proto", wgin.Wraps(h.List,
+ wrapper.InputType(reflect.TypeOf(ListInput{}))))
+ r.POST("/apisix/admin/proto", wgin.Wraps(h.Create,
+ wrapper.InputType(reflect.TypeOf(entity.Proto{}))))
+ r.PUT("/apisix/admin/proto", wgin.Wraps(h.Update,
+ wrapper.InputType(reflect.TypeOf(UpdateInput{}))))
+ r.PUT("/apisix/admin/proto/:id", wgin.Wraps(h.Update,
+ wrapper.InputType(reflect.TypeOf(UpdateInput{}))))
+ r.PATCH("/apisix/admin/proto/:id", wgin.Wraps(h.Patch,
+ wrapper.InputType(reflect.TypeOf(PatchInput{}))))
+ r.PATCH("/apisix/admin/proto/:id/*path", wgin.Wraps(h.Patch,
+ wrapper.InputType(reflect.TypeOf(PatchInput{}))))
+ r.DELETE("/apisix/admin/proto/:ids", wgin.Wraps(h.BatchDelete,
+ wrapper.InputType(reflect.TypeOf(BatchDeleteInput{}))))
+}
+
+var plugins = []string{"grpc-transcode"}
+
+type GetInput struct {
+ ID string `auto_read:"id,path" validate:"required"`
+}
+
+func (h *Handler) Get(c droplet.Context) (interface{}, error) {
+ input := c.Input().(*GetInput)
+
+ r, err := h.protoStore.Get(c.Context(), input.ID)
+ if err != nil {
+ return handler.SpecCodeResponse(err), err
+ }
+
+ return r, nil
+}
+
+type ListInput struct {
+ Desc string `auto_read:"desc,query"`
+ store.Pagination
+}
+
+func (h *Handler) List(c droplet.Context) (interface{}, error) {
+ input := c.Input().(*ListInput)
+
+ ret, err := h.protoStore.List(c.Context(), store.ListInput{
+ Predicate: func(obj interface{}) bool {
+ if input.Desc != "" {
+ return strings.Contains(obj.(*entity.Proto).Desc, input.Desc)
+ }
+ return true
+ },
+ PageSize: input.PageSize,
+ PageNumber: input.PageNumber,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return ret, nil
+}
+
+func (h *Handler) Create(c droplet.Context) (interface{}, error) {
+ input := c.Input().(*entity.Proto)
+
+ // check proto id exist
+ if input.ID != nil {
+ protoID := utils.InterfaceToString(input.ID)
+ ret, err := h.protoStore.Get(c.Context(), protoID)
+ if err != nil && err != data.ErrNotFound {
+ return handler.SpecCodeResponse(err), err
+ }
+ if ret != nil {
+ return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest}, errors.New("proto id exists")
+ }
+ }
+
+ // create
+ ret, err := h.protoStore.Create(c.Context(), input)
+ if err != nil {
+ return handler.SpecCodeResponse(err), err
+ }
+
+ return ret, nil
+}
+
+type UpdateInput struct {
+ ID string `auto_read:"id,path"`
+ entity.Proto
+}
+
+func (h *Handler) Update(c droplet.Context) (interface{}, error) {
+ input := c.Input().(*UpdateInput)
+
+ // check if ID in body is equal ID in path
+ if err := handler.IDCompare(input.ID, input.Proto.ID); err != nil {
+ return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest}, err
+ }
+
+ if input.ID != "" {
+ input.Proto.ID = input.ID
+ }
+
+ res, err := h.protoStore.Update(c.Context(), &input.Proto, true)
+ if err != nil {
+ return handler.SpecCodeResponse(err), err
+ }
+
+ return res, nil
+}
+
+type PatchInput struct {
+ ID string `auto_read:"id,path"`
+ SubPath string `auto_read:"path,path"`
+ Body []byte `auto_read:"@body"`
+}
+
+func (h *Handler) Patch(c droplet.Context) (interface{}, error) {
+ input := c.Input().(*PatchInput)
+ reqBody := input.Body
+ id := input.ID
+ subPath := input.SubPath
+
+ stored, err := h.protoStore.Get(c.Context(), id)
+ if err != nil {
+ return handler.SpecCodeResponse(err), err
+ }
+
+ res, err := utils.MergePatch(stored, subPath, reqBody)
+
+ if err != nil {
+ return handler.SpecCodeResponse(err), err
+ }
+
+ var proto entity.Proto
+ if err := json.Unmarshal(res, &proto); err != nil {
+ return handler.SpecCodeResponse(err), err
+ }
+
+ ret, err := h.protoStore.Update(c.Context(), &proto, false)
+ if err != nil {
+ return handler.SpecCodeResponse(err), err
+ }
+
+ return ret, nil
+}
+
+type BatchDeleteInput struct {
+ IDs string `auto_read:"ids,path"`
+}
+
+func (h *Handler) BatchDelete(c droplet.Context) (interface{}, error) {
+ input := c.Input().(*BatchDeleteInput)
+
+ ids := strings.Split(input.IDs, ",")
+ checklist := []store.Interface{h.routeStore, h.consumerStore, h.serviceStore, h.pluginConfigStore, h.globalRuleStore}
+
+ for _, id := range ids {
+ for _, store := range checklist {
+ if err := h.checkProtoUsed(c.Context(), store, id); err != nil {
+ return handler.SpecCodeResponse(err), err
+ }
+ }
+ }
+
+ if err := h.protoStore.BatchDelete(c.Context(), ids); err != nil {
+ return handler.SpecCodeResponse(err), err
+ }
+
+ return nil, nil
+}
+
+func (h *Handler) checkProtoUsed(ctx context.Context, storeInterface store.Interface, key string) error {
+ ret, err := storeInterface.List(ctx, store.ListInput{
+ Predicate: func(obj interface{}) bool {
+ record := obj.(entity.GetPlugins)
+ for _, plugin := range plugins {
+ if _, ok := record.GetPlugins()[plugin]; ok {
+ configs := record.GetPlugins()[plugin].(map[string]interface{})
+ protoId := utils.InterfaceToString(configs["proto_id"])
+ if protoId == key {
+ return true
+ }
+ }
+ }
+ return false
+ },
+ Format: func(obj interface{}) interface{} {
+ return obj.(entity.GetPlugins)
+ },
+ PageSize: 0,
+ PageNumber: 0,
+ })
+ if err != nil {
+ return err
+ }
+ if ret.TotalSize > 0 {
+ return fmt.Errorf("proto used check invalid: %s: %s is using this proto", storeInterface.Type(), ret.Rows[0].(entity.GetBaseInfo).GetBaseInfo().ID)
+ }
+ return nil
+
+}
diff --git a/api/internal/handler/proto/proto_test.go b/api/internal/handler/proto/proto_test.go
new file mode 100644
index 0000000..942dacd
--- /dev/null
+++ b/api/internal/handler/proto/proto_test.go
@@ -0,0 +1,47 @@
+/*
+ * 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 proto
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
+ "github.com/apisix/manager-api/internal/core/entity"
+)
+
+func TestStructUnmarshal(t *testing.T) {
+ // define and parse data
+ jsonStr := `{
+ "id": 1,
+ "create_time": 1700000000,
+ "update_time": 1700000000,
+ "desc": "desc",
+ "content": "content"
+}`
+ proto := entity.Proto{}
+ err := json.Unmarshal([]byte(jsonStr), &proto)
+
+ // asserts
+ assert.Nil(t, err)
+ assert.Equal(t, proto.ID, float64(1))
+ assert.Equal(t, proto.CreateTime, int64(1700000000))
+ assert.Equal(t, proto.UpdateTime, int64(1700000000))
+ assert.Equal(t, proto.Desc, "desc")
+ assert.Equal(t, proto.Content, "content")
+}
diff --git a/api/internal/route.go b/api/internal/route.go
index 06aa690..6a507cc 100644
--- a/api/internal/route.go
+++ b/api/internal/route.go
@@ -35,6 +35,7 @@ import (
"github.com/apisix/manager-api/internal/handler/label"
"github.com/apisix/manager-api/internal/handler/migrate"
"github.com/apisix/manager-api/internal/handler/plugin_config"
+ "github.com/apisix/manager-api/internal/handler/proto"
"github.com/apisix/manager-api/internal/handler/route"
"github.com/apisix/manager-api/internal/handler/schema"
"github.com/apisix/manager-api/internal/handler/server_info"
@@ -77,6 +78,7 @@ func SetUpRouter() *gin.Engine {
tool.NewHandler,
plugin_config.NewHandler,
migrate.NewHandler,
+ proto.NewHandler,
}
for i := range factories {
diff --git a/api/test/docker/docker-compose.yaml b/api/test/docker/docker-compose.yaml
index 4782d0e..ce4fe03 100644
--- a/api/test/docker/docker-compose.yaml
+++ b/api/test/docker/docker-compose.yaml
@@ -125,6 +125,16 @@ services:
apisix_dashboard_e2e:
ipv4_address: 172.16.238.20
+ upstream_grpc:
+ image: grpc_server_example
+ restart: always
+ ports:
+ - '50051:50051'
+ - '50052:50052'
+ networks:
+ apisix_dashboard_e2e:
+ ipv4_address: 172.16.238.21
+
apisix:
hostname: apisix_server1
image: apache/apisix:2.10.0-alpine
diff --git a/api/test/e2enew/base/base.go b/api/test/e2enew/base/base.go
index 32253ff..ced3bf8 100644
--- a/api/test/e2enew/base/base.go
+++ b/api/test/e2enew/base/base.go
@@ -37,6 +37,7 @@ var (
token string
UpstreamIp = "172.16.238.20"
+ UpstreamGrpcIp = "172.16.238.21"
APISIXHost = "http://127.0.0.1:9080"
APISIXInternalUrl = "http://172.16.238.30:9080"
APISIXSingleWorkerHost = "http://127.0.0.1:9081"
diff --git a/api/test/e2enew/proto/proto_suite_test.go b/api/test/e2enew/proto/proto_suite_test.go
new file mode 100644
index 0000000..ab0b826
--- /dev/null
+++ b/api/test/e2enew/proto/proto_suite_test.go
@@ -0,0 +1,40 @@
+/*
+ * 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 proto
+
+import (
+ "testing"
+ "time"
+
+ "github.com/onsi/ginkgo"
+ "github.com/onsi/gomega"
+
+ "github.com/apisix/manager-api/test/e2enew/base"
+)
+
+func TestRoute(t *testing.T) {
+ gomega.RegisterFailHandler(ginkgo.Fail)
+ ginkgo.RunSpecs(t, "proto suite")
+}
+
+var _ = ginkgo.AfterSuite(func() {
+ base.CleanResource("routes")
+ base.CleanResource("upstreams")
+ base.CleanResource("consumers")
+ base.CleanResource("proto")
+ time.Sleep(base.SleepTime)
+})
diff --git a/api/test/e2enew/proto/proto_test.go b/api/test/e2enew/proto/proto_test.go
new file mode 100644
index 0000000..0b16980
--- /dev/null
+++ b/api/test/e2enew/proto/proto_test.go
@@ -0,0 +1,255 @@
+/*
+ * 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 proto
+
+import (
+ "encoding/json"
+ "net/http"
+
+ "github.com/onsi/ginkgo"
+ "github.com/onsi/gomega"
+
+ "github.com/apisix/manager-api/test/e2enew/base"
+)
+
+var correctProtobuf = `syntax = "proto3";
+ package helloworld;
+ service Greeter {
+ rpc SayHello (HelloRequest) returns (HelloReply) {}
+ }
+ message HelloRequest {
+ string name = 1;
+ }
+ message HelloReply {
+ string message = 1;
+ }`
+
+var _ = ginkgo.Describe("Proto", func() {
+ ginkgo.It("create proto success", func() {
+ createProtoBody := make(map[string]interface{})
+ createProtoBody["id"] = 1
+ createProtoBody["desc"] = "test_proto1"
+ createProtoBody["content"] = correctProtobuf
+
+ _createProtoBody, err := json.Marshal(createProtoBody)
+ gomega.Expect(err).To(gomega.BeNil())
+
+ base.RunTestCase(base.HttpTestCase{
+ Object: base.ManagerApiExpect(),
+ Method: http.MethodPost,
+ Path: "/apisix/admin/proto",
+ Body: string(_createProtoBody),
+ Headers: map[string]string{"Authorization": base.GetToken()},
+ ExpectStatus: http.StatusOK,
+ })
+ })
+ ginkgo.It("create proto failed, id existed", func() {
+ createProtoBody := make(map[string]interface{})
+ createProtoBody["id"] = 1
+ createProtoBody["desc"] = "test_proto1"
+ createProtoBody["content"] = correctProtobuf
+
+ _createProtoBody, err := json.Marshal(createProtoBody)
+ gomega.Expect(err).To(gomega.BeNil())
+
+ base.RunTestCase(base.HttpTestCase{
+ Object: base.ManagerApiExpect(),
+ Method: http.MethodPost,
+ Path: "/apisix/admin/proto",
+ Body: string(_createProtoBody),
+ Headers: map[string]string{"Authorization": base.GetToken()},
+ ExpectBody: "proto id exists",
+ ExpectStatus: http.StatusBadRequest,
+ })
+ })
+ ginkgo.It("update proto success", func() {
+ updateProtoBody := make(map[string]interface{})
+ updateProtoBody["id"] = 1
+ updateProtoBody["desc"] = "test_proto1_modify"
+ updateProtoBody["content"] = correctProtobuf
+
+ _updateProtoBody, err := json.Marshal(updateProtoBody)
+ gomega.Expect(err).To(gomega.BeNil())
+
+ base.RunTestCase(base.HttpTestCase{
+ Object: base.ManagerApiExpect(),
+ Method: http.MethodPut,
+ Path: "/apisix/admin/proto",
+ Body: string(_updateProtoBody),
+ Headers: map[string]string{"Authorization": base.GetToken()},
+ ExpectBody: "test_proto1_modify",
+ ExpectStatus: http.StatusOK,
+ })
+ })
+ ginkgo.It("list proto", func() {
+ base.RunTestCase(base.HttpTestCase{
+ Object: base.ManagerApiExpect(),
+ Method: http.MethodGet,
+ Path: "/apisix/admin/proto",
+ Headers: map[string]string{"Authorization": base.GetToken()},
+ ExpectBody: "test_proto1_modify",
+ ExpectStatus: http.StatusOK,
+ })
+ })
+ ginkgo.It("get proto", func() {
+ base.RunTestCase(base.HttpTestCase{
+ Object: base.ManagerApiExpect(),
+ Method: http.MethodGet,
+ Path: "/apisix/admin/proto/1",
+ Headers: map[string]string{"Authorization": base.GetToken()},
+ ExpectBody: "test_proto1_modify",
+ ExpectStatus: http.StatusOK,
+ })
+ })
+ ginkgo.It("delete not existed proto", func() {
+ base.RunTestCase(base.HttpTestCase{
+ Object: base.ManagerApiExpect(),
+ Method: http.MethodDelete,
+ Path: "/apisix/admin/proto/not-exist",
+ Headers: map[string]string{"Authorization": base.GetToken()},
+ ExpectStatus: http.StatusNotFound,
+ })
+ })
+ ginkgo.It("delete proto", func() {
+ base.RunTestCase(base.HttpTestCase{
+ Object: base.ManagerApiExpect(),
+ Method: http.MethodDelete,
+ Path: "/apisix/admin/proto/1",
+ Headers: map[string]string{"Authorization": base.GetToken()},
+ ExpectStatus: http.StatusOK,
+ })
+ })
+})
+
+var _ = ginkgo.Describe("Proto with grpc-transcode plugin", func() {
+ ginkgo.It("create proto success", func() {
+ createProtoBody := make(map[string]interface{})
+ createProtoBody["id"] = 1
+ createProtoBody["desc"] = "test_proto1"
+ createProtoBody["content"] = correctProtobuf
+
+ _createProtoBody, err := json.Marshal(createProtoBody)
+ gomega.Expect(err).To(gomega.BeNil())
+
+ base.RunTestCase(base.HttpTestCase{
+ Object: base.ManagerApiExpect(),
+ Method: http.MethodPost,
+ Path: "/apisix/admin/proto",
+ Body: string(_createProtoBody),
+ Headers: map[string]string{"Authorization": base.GetToken()},
+ ExpectStatus: http.StatusOK,
+ })
+ })
+ ginkgo.It("create route with grpc-transcode", func() {
+ createRouteBody := make(map[string]interface{})
+ createRouteBody["id"] = 1
+ createRouteBody["name"] = "test_route"
+ createRouteBody["uri"] = "/grpc_test"
+ createRouteBody["methods"] = []string{"GET", "POST"}
+ createRouteBody["upstream"] = map[string]interface{}{
+ "nodes": []map[string]interface{}{
+ {
+ "host": base.UpstreamGrpcIp,
+ "port": 50051,
+ "weight": 1,
+ },
+ },
+ "type": "roundrobin",
+ "scheme": "grpc",
+ }
+ createRouteBody["plugins"] = map[string]interface{}{
+ "grpc-transcode": map[string]interface{}{
+ "method": "SayHello",
+ "proto_id": "1",
+ "service": "helloworld.Greeter",
+ },
+ }
+
+ _createRouteBody, err := json.Marshal(createRouteBody)
+ gomega.Expect(err).To(gomega.BeNil())
+
+ base.RunTestCase(base.HttpTestCase{
+ Object: base.ManagerApiExpect(),
+ Method: http.MethodPost,
+ Path: "/apisix/admin/routes",
+ Body: string(_createRouteBody),
+ Headers: map[string]string{"Authorization": base.GetToken()},
+ ExpectStatus: http.StatusOK,
+ })
+ })
+ ginkgo.It("hit GET route for grpc-transcode test", func() {
+ base.RunTestCase(base.HttpTestCase{
+ Object: base.APISIXExpect(),
+ Method: http.MethodGet,
+ Path: "/grpc_test",
+ Query: "name=world",
+ Headers: map[string]string{"Authorization": base.GetToken()},
+ ExpectBody: "{\"message\":\"Hello world\"}",
+ ExpectStatus: http.StatusOK,
+ })
+ })
+ ginkgo.It("hit POST route for grpc-transcode test", func() {
+ base.RunTestCase(base.HttpTestCase{
+ Object: base.APISIXExpect(),
+ Method: http.MethodPost,
+ Path: "/grpc_test",
+ Body: "name=world",
+ Headers: map[string]string{"Authorization": base.GetToken()},
+ ExpectBody: "{\"message\":\"Hello world\"}",
+ ExpectStatus: http.StatusOK,
+ })
+ })
+ ginkgo.It("hit JSON POST route for grpc-transcode test", func() {
+ base.RunTestCase(base.HttpTestCase{
+ Object: base.APISIXExpect(),
+ Method: http.MethodPost,
+ Path: "/grpc_test",
+ Body: "{\"name\": \"world\"}",
+ Headers: map[string]string{"Authorization": base.GetToken()},
+ ExpectBody: "{\"message\":\"Hello world\"}",
+ ExpectStatus: http.StatusOK,
+ })
+ })
+ ginkgo.It("delete route used proto", func() {
+ base.RunTestCase(base.HttpTestCase{
+ Object: base.ManagerApiExpect(),
+ Method: http.MethodDelete,
+ Path: "/apisix/admin/proto/1",
+ Headers: map[string]string{"Authorization": base.GetToken()},
+ ExpectBody: "proto used check invalid: route",
+ ExpectStatus: http.StatusBadRequest,
+ })
+ })
+ ginkgo.It("delete conflict route", func() {
+ base.RunTestCase(base.HttpTestCase{
+ Object: base.ManagerApiExpect(),
+ Method: http.MethodDelete,
+ Path: "/apisix/admin/routes/1",
+ Headers: map[string]string{"Authorization": base.GetToken()},
+ ExpectStatus: http.StatusOK,
+ })
+ })
+ ginkgo.It("delete proto again", func() {
+ base.RunTestCase(base.HttpTestCase{
+ Object: base.ManagerApiExpect(),
+ Method: http.MethodDelete,
+ Path: "/apisix/admin/proto/1",
+ Headers: map[string]string{"Authorization": base.GetToken()},
+ ExpectStatus: http.StatusOK,
+ })
+ })
+})