Skip to content

Commit

Permalink
feat: Replace gorilla/mux router library with Echo
Browse files Browse the repository at this point in the history
Fix #4247. Replace gorilla/mux router library with Echo.

Signed-off-by: Lindsey Cheng <beckysocute@gmail.com>
  • Loading branch information
lindseysimple committed Aug 3, 2023
1 parent e9743d5 commit f0b0b88
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 96 deletions.
4 changes: 2 additions & 2 deletions cmd/core-command/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ import (

"github.com/edgexfoundry/edgex-go/internal/core/command"

"github.com/gorilla/mux"
"github.com/labstack/echo/v4"
)

func main() {
ctx, cancel := context.WithCancel(context.Background())
command.Main(ctx, cancel, mux.NewRouter())
command.Main(ctx, cancel, echo.New())
}
6 changes: 6 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/gomodule/redigo v1.8.9
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.8.0
github.com/labstack/echo/v4 v4.10.2
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475
github.com/spiffe/go-spiffe/v2 v2.1.6
github.com/stretchr/testify v1.8.4
Expand All @@ -36,6 +37,7 @@ require (
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.1 // indirect
github.com/go-redis/redis/v7 v7.3.0 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/consul/api v1.23.0 // indirect
Expand All @@ -49,6 +51,7 @@ require (
github.com/hashicorp/serf v0.10.1 // indirect
github.com/klauspost/compress v1.16.5 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/labstack/gommon v0.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mitchellh/consulstructure v0.0.0-20190329231841-56fdc4d2da54 // indirect
Expand All @@ -61,6 +64,8 @@ require (
github.com/nats-io/nuid v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/zeebo/errs v1.3.0 // indirect
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
Expand All @@ -69,6 +74,7 @@ require (
golang.org/x/sync v0.2.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.6.0 // indirect
google.golang.org/genproto v0.0.0-20230223222841-637eb2293923 // indirect
google.golang.org/grpc v1.53.0 // indirect
Expand Down
13 changes: 13 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ github.com/go-redis/redis/v7 v7.3.0 h1:3oHqd0W7f/VLKBxeYTEpqdMUsmMectngjM9OtoRoI
github.com/go-redis/redis/v7 v7.3.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
Expand Down Expand Up @@ -151,6 +153,10 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
Expand Down Expand Up @@ -244,6 +250,11 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs=
Expand Down Expand Up @@ -309,6 +320,7 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
Expand Down Expand Up @@ -339,5 +351,6 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
71 changes: 34 additions & 37 deletions internal/core/command/controller/http/command.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (C) 2021 IOTech Ltd
// Copyright (C) 2021-2023 IOTech Ltd
//
// SPDX-License-Identifier: Apache-2.0

Expand All @@ -21,7 +21,7 @@ import (
"github.com/edgexfoundry/edgex-go/internal/pkg"
"github.com/edgexfoundry/edgex-go/internal/pkg/utils"

"github.com/gorilla/mux"
"github.com/labstack/echo/v4"
)

type CommandController struct {
Expand All @@ -35,47 +35,47 @@ func NewCommandController(dic *di.Container) *CommandController {
}
}

func (cc *CommandController) AllCommands(w http.ResponseWriter, r *http.Request) {
func (cc *CommandController) AllCommands(c echo.Context) error {
lc := container.LoggingClientFrom(cc.dic.Get)
r := c.Request()
w := c.Response()
ctx := r.Context()
config := commandContainer.ConfigurationFrom(cc.dic.Get)

// parse URL query string for offset, limit
offset, limit, _, err := utils.ParseGetAllObjectsRequestQueryString(r, 0, math.MaxInt32, -1, config.Service.MaxResultCount)
if err != nil {
utils.WriteErrorResponse(w, ctx, lc, err, "")
return
return utils.WriteErrorResponse(w, ctx, lc, err, "")

}
commands, totalCount, err := application.AllCommands(offset, limit, cc.dic)
if err != nil {
utils.WriteErrorResponse(w, ctx, lc, err, "")
return
return utils.WriteErrorResponse(w, ctx, lc, err, "")
}

response := responseDTO.NewMultiDeviceCoreCommandsResponse("", "", http.StatusOK, totalCount, commands)
utils.WriteHttpHeader(w, ctx, http.StatusOK)
// encode and send out the response
pkg.EncodeAndWriteResponse(response, w, lc)
return pkg.EncodeAndWriteResponse(response, w, lc)
}

func (cc *CommandController) CommandsByDeviceName(w http.ResponseWriter, r *http.Request) {
func (cc *CommandController) CommandsByDeviceName(c echo.Context) error {
lc := container.LoggingClientFrom(cc.dic.Get)
r := c.Request()
w := c.Response()
ctx := r.Context()

// URL parameters
vars := mux.Vars(r)
name := vars[common.Name]

name := c.Param(common.Name)
deviceCoreCommand, err := application.CommandsByDeviceName(name, cc.dic)
if err != nil {
utils.WriteErrorResponse(w, ctx, lc, err, "")
return
return utils.WriteErrorResponse(w, ctx, lc, err, "")
}

response := responseDTO.NewDeviceCoreCommandResponse("", "", http.StatusOK, deviceCoreCommand)
utils.WriteHttpHeader(w, ctx, http.StatusOK)
// encode and send out the response
pkg.EncodeAndWriteResponse(response, w, lc)
return pkg.EncodeAndWriteResponse(response, w, lc)
}

func validateGetCommandParameters(r *http.Request) (err errors.EdgeX) {
Expand All @@ -90,63 +90,60 @@ func validateGetCommandParameters(r *http.Request) (err errors.EdgeX) {
return nil
}

func (cc *CommandController) IssueGetCommandByName(w http.ResponseWriter, r *http.Request) {
func (cc *CommandController) IssueGetCommandByName(c echo.Context) error {
lc := container.LoggingClientFrom(cc.dic.Get)
r := c.Request()
w := c.Response()
ctx := r.Context()

// URL parameters
vars := mux.Vars(r)
deviceName := vars[common.Name]
commandName := vars[common.Command]
deviceName := c.Param(common.Name)
commandName := c.Param(common.Command)

// Query params
queryParams := r.URL.RawQuery
err := validateGetCommandParameters(r)
if err != nil {
utils.WriteErrorResponse(w, ctx, lc, err, "")
return
return utils.WriteErrorResponse(w, ctx, lc, err, "")
}

response, err := application.IssueGetCommandByName(deviceName, commandName, queryParams, cc.dic)
if err != nil {
utils.WriteErrorResponse(w, ctx, lc, err, "")
return
return utils.WriteErrorResponse(w, ctx, lc, err, "")
}
// encode and send out the response
if response != nil {
utils.WriteHttpHeader(w, ctx, response.StatusCode)
pkg.EncodeAndWriteResponse(response, w, lc)
} else {
// If dsReturnEvent is no, there will be no content returned in the http response
utils.WriteHttpHeader(w, ctx, http.StatusOK)
return pkg.EncodeAndWriteResponse(response, w, lc)
}
// If dsReturnEvent is no, there will be no content returned in the http response
utils.WriteHttpHeader(w, ctx, http.StatusOK)
return nil
}

func (cc *CommandController) IssueSetCommandByName(w http.ResponseWriter, r *http.Request) {
func (cc *CommandController) IssueSetCommandByName(c echo.Context) error {
lc := container.LoggingClientFrom(cc.dic.Get)
r := c.Request()
w := c.Response()
ctx := r.Context()

// URL parameters
vars := mux.Vars(r)
deviceName := vars[common.Name]
commandName := vars[common.Command]

deviceName := c.Param(common.Name)
commandName := c.Param(common.Command)
// Query params
queryParams := r.URL.RawQuery

// Request body
settings, err := utils.ParseBodyToMap(r)
if err != nil {
utils.WriteErrorResponse(w, ctx, lc, err, "")
return
return utils.WriteErrorResponse(w, ctx, lc, err, "")
}
response, err := application.IssueSetCommandByName(deviceName, commandName, queryParams, settings, cc.dic)
if err != nil {
utils.WriteErrorResponse(w, ctx, lc, err, "")
return
return utils.WriteErrorResponse(w, ctx, lc, err, "")
}

utils.WriteHttpHeader(w, ctx, response.StatusCode)
// encode and send out the response
pkg.EncodeAndWriteResponse(response, w, lc)
return pkg.EncodeAndWriteResponse(response, w, lc)
}
57 changes: 38 additions & 19 deletions internal/core/command/controller/http/command_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (C) 2021 IOTech Ltd
// Copyright (C) 2021-2023 IOTech Ltd
//
// SPDX-License-Identifier: Apache-2.0

Expand Down Expand Up @@ -30,7 +30,7 @@ import (
"github.com/edgexfoundry/go-mod-core-contracts/v3/errors"

"github.com/google/uuid"
"github.com/gorilla/mux"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -217,6 +217,7 @@ func TestAllCommands(t *testing.T) {
}
for _, testCase := range tests {
t.Run(testCase.name, func(t *testing.T) {
e := echo.New()
req, err := http.NewRequest(http.MethodGet, common.ApiAllDeviceRoute, http.NoBody)
query := req.URL.Query()
if testCase.offset != "" {
Expand All @@ -230,8 +231,10 @@ func TestAllCommands(t *testing.T) {

// Act
recorder := httptest.NewRecorder()
handler := http.HandlerFunc(cc.AllCommands)
handler.ServeHTTP(recorder, req)
handler := echo.HandlerFunc(cc.AllCommands)
c := e.NewContext(req, recorder)
err = handler(c)
assert.NoError(t, err)

// Assert
if testCase.errorExpected {
Expand Down Expand Up @@ -296,14 +299,19 @@ func TestCommandsByDeviceName(t *testing.T) {
}
for _, testCase := range tests {
t.Run(testCase.name, func(t *testing.T) {
req, err := http.NewRequest(http.MethodGet, common.ApiDeviceByNameRoute, http.NoBody)
req = mux.SetURLVars(req, map[string]string{common.Name: testCase.deviceName})
require.NoError(t, err)
e := echo.New()
req := httptest.NewRequest("GET", "/api/v3/device/name/:name", nil)

// Act
recorder := httptest.NewRecorder()
handler := http.HandlerFunc(cc.CommandsByDeviceName)
handler.ServeHTTP(recorder, req)
c := e.NewContext(req, recorder)

// Set url path params
c.SetParamNames(common.Name)
c.SetParamValues(testCase.deviceName)
handler := echo.HandlerFunc(cc.CommandsByDeviceName)
err := handler(c)
assert.NoError(t, err)

// Assert
if testCase.errorExpected {
Expand Down Expand Up @@ -381,15 +389,20 @@ func TestIssueGetCommand(t *testing.T) {
}
for _, testCase := range tests {
t.Run(testCase.name, func(t *testing.T) {
req, err := http.NewRequest(http.MethodGet, common.ApiDeviceNameCommandNameRoute, http.NoBody)
e := echo.New()
req := httptest.NewRequest(http.MethodGet, "/api/v3/device/name/:name/:command", http.NoBody)
req.URL.RawQuery = testCase.queryStrings
req = mux.SetURLVars(req, map[string]string{common.Name: testCase.deviceName, common.Command: testCase.commandName})
require.NoError(t, err)

// Act
recorder := httptest.NewRecorder()
handler := http.HandlerFunc(cc.IssueGetCommandByName)
handler.ServeHTTP(recorder, req)
handler := echo.HandlerFunc(cc.IssueGetCommandByName)
c := e.NewContext(req, recorder)

// Set url path params
c.SetParamNames(common.Name, common.Command)
c.SetParamValues(testCase.deviceName, testCase.commandName)
err := handler(c)
assert.NoError(t, err)

// Assert
if testCase.errorExpected {
Expand Down Expand Up @@ -469,15 +482,21 @@ func TestIssueSetCommand(t *testing.T) {
}
for _, testCase := range tests {
t.Run(testCase.name, func(t *testing.T) {
req, err := http.NewRequest(http.MethodPut, common.ApiDeviceNameCommandNameRoute, bytes.NewBuffer(testCase.settings))
e := echo.New()
req := httptest.NewRequest(http.MethodPut, "/api/v3/device/name/:name/:command", bytes.NewBuffer(testCase.settings))
req.URL.RawQuery = testCase.queryStrings
req = mux.SetURLVars(req, map[string]string{common.Name: testCase.deviceName, common.Command: testCase.commandName})
require.NoError(t, err)

// Act
recorder := httptest.NewRecorder()
handler := http.HandlerFunc(cc.IssueSetCommandByName)
handler.ServeHTTP(recorder, req)
handler := echo.HandlerFunc(cc.IssueSetCommandByName)
c := e.NewContext(req, recorder)

// Set url path params
c.SetParamNames(common.Name, common.Command)
c.SetParamValues(testCase.deviceName, testCase.commandName)

err := handler(c)
assert.NoError(t, err)

// Assert
var res commonDTO.BaseResponse
Expand Down
7 changes: 4 additions & 3 deletions internal/core/command/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,18 @@ import (
"github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/startup"
"github.com/edgexfoundry/go-mod-bootstrap/v3/di"
clients "github.com/edgexfoundry/go-mod-core-contracts/v3/clients/http"
"github.com/gorilla/mux"

"github.com/labstack/echo/v4"
)

// Bootstrap contains references to dependencies required by the BootstrapHandler.
type Bootstrap struct {
router *mux.Router
router *echo.Echo
serviceName string
}

// NewBootstrap is a factory method that returns an initialized Bootstrap receiver struct.
func NewBootstrap(router *mux.Router, serviceName string) *Bootstrap {
func NewBootstrap(router *echo.Echo, serviceName string) *Bootstrap {
return &Bootstrap{
router: router,
serviceName: serviceName,
Expand Down
Loading

0 comments on commit f0b0b88

Please sign in to comment.