Skip to content

Commit

Permalink
restserver: change using go-chi for routing engine
Browse files Browse the repository at this point in the history
  • Loading branch information
agungdwiprasetyo committed Jun 14, 2023
1 parent 0b86d9f commit 8ecd3ab
Show file tree
Hide file tree
Showing 26 changed files with 587 additions and 446 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ main_service.go
*.key
coverage.txt
example/
cmd/candi/test/
.vscode
.idea
.DS_Store
139 changes: 70 additions & 69 deletions cmd/candi/template_delivery_rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import (
"net/http"{{if and .MongoDeps (not .SQLDeps)}}{{else}}
"strconv"{{end}}
"github.com/labstack/echo"
"{{$.PackagePrefix}}/internal/modules/{{cleanPathModule .ModuleName}}/domain"
"{{.PackagePrefix}}/pkg/shared/usecase"
Expand Down Expand Up @@ -41,109 +39,119 @@ func NewRestHandler(uc usecase.Usecase, deps dependency.Dependency) *RestHandler
// Mount handler with root "/"
// handling version in here
func (h *RestHandler) Mount(root *echo.Group) {
v1Root := root.Group(candihelper.V1)
{{camel .ModuleName}} := v1Root.Group("/{{kebab .ModuleName}}", restserver.EchoWrapMiddleware(h.mw.HTTPBearerAuth))
{{camel .ModuleName}}.GET("", h.getAll{{upper (camel .ModuleName)}}, restserver.EchoWrapMiddleware(h.mw.HTTPPermissionACL("getAll{{upper (camel .ModuleName)}}")))
{{camel .ModuleName}}.GET("/:id", h.getDetail{{upper (camel .ModuleName)}}ByID, restserver.EchoWrapMiddleware(h.mw.HTTPPermissionACL("getDetail{{upper (camel .ModuleName)}}")))
{{camel .ModuleName}}.POST("", h.create{{upper (camel .ModuleName)}}, restserver.EchoWrapMiddleware(h.mw.HTTPPermissionACL("create{{upper (camel .ModuleName)}}")))
{{camel .ModuleName}}.PUT("/:id", h.update{{upper (camel .ModuleName)}}, restserver.EchoWrapMiddleware(h.mw.HTTPPermissionACL("update{{upper (camel .ModuleName)}}")))
{{camel .ModuleName}}.DELETE("/:id", h.delete{{upper (camel .ModuleName)}}, restserver.EchoWrapMiddleware(h.mw.HTTPPermissionACL("delete{{upper (camel .ModuleName)}}")))
func (h *RestHandler) Mount(root interfaces.RESTRouter) {
v1{{upper (camel .ModuleName)}} := root.Group(candihelper.V1+"/{{kebab .ModuleName}}", h.mw.HTTPBearerAuth)
v1{{upper (camel .ModuleName)}}.GET("/", h.getAll{{upper (camel .ModuleName)}}, h.mw.HTTPPermissionACL("getAll{{upper (camel .ModuleName)}}"))
v1{{upper (camel .ModuleName)}}.GET("/:id", h.getDetail{{upper (camel .ModuleName)}}ByID, h.mw.HTTPPermissionACL("getDetail{{upper (camel .ModuleName)}}"))
v1{{upper (camel .ModuleName)}}.POST("/", h.create{{upper (camel .ModuleName)}}, h.mw.HTTPPermissionACL("create{{upper (camel .ModuleName)}}"))
v1{{upper (camel .ModuleName)}}.PUT("/:id", h.update{{upper (camel .ModuleName)}}, h.mw.HTTPPermissionACL("update{{upper (camel .ModuleName)}}"))
v1{{upper (camel .ModuleName)}}.DELETE("/:id", h.delete{{upper (camel .ModuleName)}}, h.mw.HTTPPermissionACL("delete{{upper (camel .ModuleName)}}"))
}
func (h *RestHandler) getAll{{upper (camel .ModuleName)}}(c echo.Context) error {
trace, ctx := tracer.StartTraceWithContext(c.Request().Context(), "{{upper (camel .ModuleName)}}DeliveryREST:GetAll{{upper (camel .ModuleName)}}")
func (h *RestHandler) getAll{{upper (camel .ModuleName)}}(rw http.ResponseWriter, req *http.Request) {
trace, ctx := tracer.StartTraceWithContext(req.Context(), "{{upper (camel .ModuleName)}}DeliveryREST:GetAll{{upper (camel .ModuleName)}}")
defer trace.Finish()
tokenClaim := candishared.ParseTokenClaimFromContext(ctx) // must using HTTPBearerAuth in middleware for this handler
var filter domain.Filter{{upper (camel .ModuleName)}}
if err := candihelper.ParseFromQueryParam(c.Request().URL.Query(), &filter); err != nil {
return wrapper.NewHTTPResponse(http.StatusBadRequest, "Failed parse filter", err).JSON(c.Response())
if err := candihelper.ParseFromQueryParam(req.URL.Query(), &filter); err != nil {
wrapper.NewHTTPResponse(http.StatusBadRequest, "Failed parse filter", err).JSON(rw)
return
}
if err := h.validator.ValidateDocument("{{cleanPathModule .ModuleName}}/get_all", filter); err != nil {
return wrapper.NewHTTPResponse(http.StatusBadRequest, "Failed validate filter", err).JSON(c.Response())
wrapper.NewHTTPResponse(http.StatusBadRequest, "Failed validate filter", err).JSON(rw)
return
}
data, meta, err := h.uc.{{upper (camel .ModuleName)}}().GetAll{{upper (camel .ModuleName)}}(ctx, &filter)
if err != nil {
return wrapper.NewHTTPResponse(http.StatusBadRequest, err.Error()).JSON(c.Response())
wrapper.NewHTTPResponse(http.StatusBadRequest, err.Error()).JSON(rw)
return
}
message := "Success, with your user id (" + tokenClaim.Subject + ") and role (" + tokenClaim.Role + ")"
return wrapper.NewHTTPResponse(http.StatusOK, message, meta, data).JSON(c.Response())
wrapper.NewHTTPResponse(http.StatusOK, message, meta, data).JSON(rw)
}
func (h *RestHandler) getDetail{{upper (camel .ModuleName)}}ByID(c echo.Context) error {
trace, ctx := tracer.StartTraceWithContext(c.Request().Context(), "{{upper (camel .ModuleName)}}DeliveryREST:GetDetail{{upper (camel .ModuleName)}}ByID")
func (h *RestHandler) getDetail{{upper (camel .ModuleName)}}ByID(rw http.ResponseWriter, req *http.Request) {
trace, ctx := tracer.StartTraceWithContext(req.Context(), "{{upper (camel .ModuleName)}}DeliveryREST:GetDetail{{upper (camel .ModuleName)}}ByID")
defer trace.Finish()
{{if and .MongoDeps (not .SQLDeps)}}id := c.Param("id"){{else}}id, _ := strconv.Atoi(c.Param("id")){{end}}
{{if and .MongoDeps (not .SQLDeps)}}id := restserver.URLParam(req, "id"){{else}}id, _ := strconv.Atoi(restserver.URLParam(req, "id")){{end}}
data, err := h.uc.{{upper (camel .ModuleName)}}().GetDetail{{upper (camel .ModuleName)}}(ctx, id)
if err != nil {
return wrapper.NewHTTPResponse(http.StatusBadRequest, err.Error()).JSON(c.Response())
wrapper.NewHTTPResponse(http.StatusBadRequest, err.Error()).JSON(rw)
return
}
return wrapper.NewHTTPResponse(http.StatusOK, "Success", data).JSON(c.Response())
wrapper.NewHTTPResponse(http.StatusOK, "Success", data).JSON(rw)
}
func (h *RestHandler) create{{upper (camel .ModuleName)}}(c echo.Context) error {
trace, ctx := tracer.StartTraceWithContext(c.Request().Context(), "{{upper (camel .ModuleName)}}DeliveryREST:Create{{upper (camel .ModuleName)}}")
func (h *RestHandler) create{{upper (camel .ModuleName)}}(rw http.ResponseWriter, req *http.Request) {
trace, ctx := tracer.StartTraceWithContext(req.Context(), "{{upper (camel .ModuleName)}}DeliveryREST:Create{{upper (camel .ModuleName)}}")
defer trace.Finish()
body, _ := io.ReadAll(c.Request().Body)
body, _ := io.ReadAll(req.Body)
if err := h.validator.ValidateDocument("{{cleanPathModule .ModuleName}}/save", body); err != nil {
return wrapper.NewHTTPResponse(http.StatusBadRequest, "Failed validate payload", err).JSON(c.Response())
wrapper.NewHTTPResponse(http.StatusBadRequest, "Failed validate payload", err).JSON(rw)
return
}
var payload domain.Request{{upper (camel .ModuleName)}}
if err := json.Unmarshal(body, &payload); err != nil {
return wrapper.NewHTTPResponse(http.StatusBadRequest, err.Error()).JSON(c.Response())
wrapper.NewHTTPResponse(http.StatusBadRequest, err.Error()).JSON(rw)
return
}
res, err := h.uc.{{upper (camel .ModuleName)}}().Create{{upper (camel .ModuleName)}}(ctx, &payload)
if err != nil {
return wrapper.NewHTTPResponse(http.StatusBadRequest, err.Error()).JSON(c.Response())
wrapper.NewHTTPResponse(http.StatusBadRequest, err.Error()).JSON(rw)
return
}
return wrapper.NewHTTPResponse(http.StatusCreated, "Success", res).JSON(c.Response())
wrapper.NewHTTPResponse(http.StatusCreated, "Success", res).JSON(rw)
}
func (h *RestHandler) update{{upper (camel .ModuleName)}}(c echo.Context) error {
trace, ctx := tracer.StartTraceWithContext(c.Request().Context(), "{{upper (camel .ModuleName)}}DeliveryREST:Update{{upper (camel .ModuleName)}}")
func (h *RestHandler) update{{upper (camel .ModuleName)}}(rw http.ResponseWriter, req *http.Request) {
trace, ctx := tracer.StartTraceWithContext(req.Context(), "{{upper (camel .ModuleName)}}DeliveryREST:Update{{upper (camel .ModuleName)}}")
defer trace.Finish()
body, _ := io.ReadAll(c.Request().Body)
body, _ := io.ReadAll(req.Body)
if err := h.validator.ValidateDocument("{{cleanPathModule .ModuleName}}/save", body); err != nil {
return wrapper.NewHTTPResponse(http.StatusBadRequest, "Failed validate payload", err).JSON(c.Response())
wrapper.NewHTTPResponse(http.StatusBadRequest, "Failed validate payload", err).JSON(rw)
return
}
var payload domain.Request{{upper (camel .ModuleName)}}
if err := json.Unmarshal(body, &payload); err != nil {
return wrapper.NewHTTPResponse(http.StatusBadRequest, err.Error()).JSON(c.Response())
wrapper.NewHTTPResponse(http.StatusBadRequest, err.Error()).JSON(rw)
return
}
{{if and .MongoDeps (not .SQLDeps)}}payload.ID = c.Param("id"){{else}}payload.ID, _ = strconv.Atoi(c.Param("id")){{end}}
{{if and .MongoDeps (not .SQLDeps)}}payload.ID = restserver.URLParam(req, "id"){{else}}payload.ID, _ = strconv.Atoi(restserver.URLParam(req, "id")){{end}}
err := h.uc.{{upper (camel .ModuleName)}}().Update{{upper (camel .ModuleName)}}(ctx, &payload)
if err != nil {
return wrapper.NewHTTPResponse(http.StatusBadRequest, err.Error()).JSON(c.Response())
wrapper.NewHTTPResponse(http.StatusBadRequest, err.Error()).JSON(rw)
return
}
return wrapper.NewHTTPResponse(http.StatusOK, "Success").JSON(c.Response())
wrapper.NewHTTPResponse(http.StatusOK, "Success").JSON(rw)
}
func (h *RestHandler) delete{{upper (camel .ModuleName)}}(c echo.Context) error {
trace, ctx := tracer.StartTraceWithContext(c.Request().Context(), "{{upper (camel .ModuleName)}}DeliveryREST:Delete{{upper (camel .ModuleName)}}")
func (h *RestHandler) delete{{upper (camel .ModuleName)}}(rw http.ResponseWriter, req *http.Request) {
trace, ctx := tracer.StartTraceWithContext(req.Context(), "{{upper (camel .ModuleName)}}DeliveryREST:Delete{{upper (camel .ModuleName)}}")
defer trace.Finish()
{{if and .MongoDeps (not .SQLDeps)}}id := c.Param("id"){{else}}id, _ := strconv.Atoi(c.Param("id")){{end}}
{{if and .MongoDeps (not .SQLDeps)}}id := restserver.URLParam(req, "id"){{else}}id, _ := strconv.Atoi(restserver.URLParam(req, "id")){{end}}
if err := h.uc.{{upper (camel .ModuleName)}}().Delete{{upper (camel .ModuleName)}}(ctx, id); err != nil {
return wrapper.NewHTTPResponse(http.StatusBadRequest, err.Error()).JSON(c.Response())
wrapper.NewHTTPResponse(http.StatusBadRequest, err.Error()).JSON(rw)
return
}
return wrapper.NewHTTPResponse(http.StatusOK, "Success").JSON(c.Response())
wrapper.NewHTTPResponse(http.StatusOK, "Success").JSON(rw)
}
`

Expand All @@ -162,10 +170,10 @@ import (
mockusecase "{{$.PackagePrefix}}/pkg/mocks/modules/{{cleanPathModule .ModuleName}}/usecase"
mocksharedusecase "{{$.PackagePrefix}}/pkg/mocks/shared/usecase"
"{{.LibraryName}}/candihelper"
"{{.LibraryName}}/candishared"
mockdeps "{{.LibraryName}}/mocks/codebase/factory/dependency"
mockinterfaces "{{.LibraryName}}/mocks/codebase/interfaces"
"github.com/labstack/echo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
Expand All @@ -192,8 +200,13 @@ func TestNewRestHandler(t *testing.T) {
handler := NewRestHandler(nil, mockDeps)
assert.NotNil(t, handler)
e := echo.New()
handler.Mount(e.Group("/"))
mockRoute := &mockinterfaces.RESTRouter{}
mockRoute.On("Group", mock.Anything, mock.Anything).Return(mockRoute)
mockRoute.On("GET", mock.Anything, mock.Anything, mock.Anything)
mockRoute.On("POST", mock.Anything, mock.Anything, mock.Anything)
mockRoute.On("PUT", mock.Anything, mock.Anything, mock.Anything)
mockRoute.On("DELETE", mock.Anything, mock.Anything, mock.Anything)
handler.Mount(mockRoute)
}
func TestRestHandler_getAll{{upper (camel .ModuleName)}}(t *testing.T) {
Expand Down Expand Up @@ -224,11 +237,9 @@ func TestRestHandler_getAll{{upper (camel .ModuleName)}}(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/"+tt.reqBody, strings.NewReader(tt.reqBody))
req = req.WithContext(candishared.SetToContext(req.Context(), candishared.ContextKeyTokenClaim, &candishared.TokenClaim{}))
req.Header.Add(echo.HeaderContentType, echo.MIMEApplicationJSON)
req.Header.Add(candihelper.HeaderContentType, candihelper.HeaderMIMEApplicationJSON)
res := httptest.NewRecorder()
echoContext := echo.New().NewContext(req, res)
err := handler.getAll{{upper (camel .ModuleName)}}(echoContext)
assert.NoError(t, err)
handler.getAll{{upper (camel .ModuleName)}}(res, req)
assert.Equal(t, tt.wantRespCode, res.Code)
})
}
Expand Down Expand Up @@ -258,11 +269,9 @@ func TestRestHandler_getDetail{{upper (camel .ModuleName)}}ByID(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(tt.reqBody))
req = req.WithContext(candishared.SetToContext(req.Context(), candishared.ContextKeyTokenClaim, &candishared.TokenClaim{}))
req.Header.Add(echo.HeaderContentType, echo.MIMEApplicationJSON)
req.Header.Add(candihelper.HeaderContentType, candihelper.HeaderMIMEApplicationJSON)
res := httptest.NewRecorder()
echoContext := echo.New().NewContext(req, res)
err := handler.getDetail{{upper (camel .ModuleName)}}ByID(echoContext)
assert.NoError(t, err)
handler.getDetail{{upper (camel .ModuleName)}}ByID(res, req)
assert.Equal(t, tt.wantRespCode, res.Code)
})
}
Expand All @@ -284,7 +293,7 @@ func TestRestHandler_create{{upper (camel .ModuleName)}}(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
{{camel .ModuleName}}Usecase := &mockusecase.{{upper (camel .ModuleName)}}Usecase{}
{{camel .ModuleName}}Usecase.On("Create{{upper (camel .ModuleName)}}", mock.Anything, mock.Anything).Return(tt.wantUsecaseError)
{{camel .ModuleName}}Usecase.On("Create{{upper (camel .ModuleName)}}", mock.Anything, mock.Anything).Return(domain.Response{{upper (camel .ModuleName)}}{}, tt.wantUsecaseError)
mockValidator := &mockinterfaces.Validator{}
mockValidator.On("ValidateDocument", mock.Anything, mock.Anything).Return(tt.wantValidateError)
Expand All @@ -294,13 +303,9 @@ func TestRestHandler_create{{upper (camel .ModuleName)}}(t *testing.T) {
handler := RestHandler{uc: uc, validator: mockValidator}
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(tt.reqBody))
req.Header.Add(echo.HeaderContentType, echo.MIMEApplicationJSON)
req.Header.Add(candihelper.HeaderContentType, candihelper.HeaderMIMEApplicationJSON)
res := httptest.NewRecorder()
echoContext := echo.New().NewContext(req, res)
echoContext.SetParamNames("id")
echoContext.SetParamValues("001")
err := handler.create{{upper (camel .ModuleName)}}(echoContext)
assert.NoError(t, err)
handler.create{{upper (camel .ModuleName)}}(res, req)
assert.Equal(t, tt.wantRespCode, res.Code)
})
}
Expand Down Expand Up @@ -336,11 +341,9 @@ func TestRestHandler_update{{upper (camel .ModuleName)}}(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(tt.reqBody))
req = req.WithContext(candishared.SetToContext(req.Context(), candishared.ContextKeyTokenClaim, &candishared.TokenClaim{}))
req.Header.Add(echo.HeaderContentType, echo.MIMEApplicationJSON)
req.Header.Add(candihelper.HeaderContentType, candihelper.HeaderMIMEApplicationJSON)
res := httptest.NewRecorder()
echoContext := echo.New().NewContext(req, res)
err := handler.update{{upper (camel .ModuleName)}}(echoContext)
assert.NoError(t, err)
handler.update{{upper (camel .ModuleName)}}(res, req)
assert.Equal(t, tt.wantRespCode, res.Code)
})
}
Expand Down Expand Up @@ -369,11 +372,9 @@ func TestRestHandler_delete{{upper (camel .ModuleName)}}(t *testing.T) {
handler := RestHandler{uc: uc, validator: mockValidator}
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(tt.reqBody))
req.Header.Add(echo.HeaderContentType, echo.MIMEApplicationJSON)
req.Header.Add(candihelper.HeaderContentType, candihelper.HeaderMIMEApplicationJSON)
res := httptest.NewRecorder()
echoContext := echo.New().NewContext(req, res)
err := handler.delete{{upper (camel .ModuleName)}}(echoContext)
assert.NoError(t, err)
handler.delete{{upper (camel .ModuleName)}}(res, req)
assert.Equal(t, tt.wantRespCode, res.Code)
})
}
Expand Down
Loading

0 comments on commit 8ecd3ab

Please sign in to comment.