From 55f5b2ee6467e5737e692d3aee2706f122dc4cba Mon Sep 17 00:00:00 2001 From: PereRohit <77770177+PereRohit@users.noreply.github.com> Date: Sun, 11 Sep 2022 16:23:41 +0530 Subject: [PATCH] 1. Moved resources out of cmd to its own internal folder 2. Template updates with abstractions over common handlers and service handler 3. Ping demo handler baked in with the generated service 4. Health endpoint auto caller for service(s) Health check --- cmd/gosvc/main.go | 4 +- .../internal/handler/handler.go.tmpl | 37 ---------- .../resources/internal/router/router.go.tmpl | 27 -------- internal/resources.go | 12 ++++ internal/resources/README.md.tmpl | 3 + .../resources/cmd/service/main.go.tmpl | 0 {cmd/gosvc => internal}/resources/go.mod.tmpl | 0 .../resources/internal/handler/common.go.tmpl | 67 +++++++++++++++++++ .../internal/handler/handler.go.tmpl | 67 +++++++++++++++++++ .../resources/internal/logic/logic.go.tmpl | 26 +++++++ .../resources/internal/logic/mockgen.go.tmpl | 4 ++ .../resources/internal/model/model.go.tmpl | 5 ++ .../resources/internal/router/router.go.tmpl | 37 ++++++++++ 13 files changed, 224 insertions(+), 65 deletions(-) delete mode 100644 cmd/gosvc/resources/internal/handler/handler.go.tmpl delete mode 100644 cmd/gosvc/resources/internal/router/router.go.tmpl create mode 100644 internal/resources.go create mode 100644 internal/resources/README.md.tmpl rename {cmd/gosvc => internal}/resources/cmd/service/main.go.tmpl (100%) rename {cmd/gosvc => internal}/resources/go.mod.tmpl (100%) create mode 100644 internal/resources/internal/handler/common.go.tmpl create mode 100644 internal/resources/internal/handler/handler.go.tmpl create mode 100644 internal/resources/internal/logic/logic.go.tmpl create mode 100644 internal/resources/internal/logic/mockgen.go.tmpl create mode 100644 internal/resources/internal/model/model.go.tmpl create mode 100644 internal/resources/internal/router/router.go.tmpl diff --git a/cmd/gosvc/main.go b/cmd/gosvc/main.go index 6efbf1f..7e6089e 100644 --- a/cmd/gosvc/main.go +++ b/cmd/gosvc/main.go @@ -14,12 +14,12 @@ import ( "strings" "text/template" + "github.com/PereRohit/gosvc/internal" "golang.org/x/text/cases" "golang.org/x/text/language" ) var ( - //go:embed resources/* f embed.FS re = regexp.MustCompile(`[^\w]`) @@ -144,6 +144,8 @@ func CommandRunner(command string, args ...string) { func main() { flag.Parse() + f = internal.GetEmbeddedFS() + initModule := strings.TrimSpace(*moduleName) if len(initModule) <= 0 { fmt.Fprintf(os.Stderr, "error: invalid usage\n") diff --git a/cmd/gosvc/resources/internal/handler/handler.go.tmpl b/cmd/gosvc/resources/internal/handler/handler.go.tmpl deleted file mode 100644 index 2c8a831..0000000 --- a/cmd/gosvc/resources/internal/handler/handler.go.tmpl +++ /dev/null @@ -1,37 +0,0 @@ -package handler - -import ( - "net/http" - - "github.com/PereRohit/util/response" -) - -type Handler interface { - MethodNotAllowed(http.ResponseWriter, *http.Request) - RouteNotFound(http.ResponseWriter, *http.Request) - HealthCheck(http.ResponseWriter, *http.Request) -} - -//go:generate mockgen --build_flags=--mod=mod --destination=./../../pkg/mocks/internal/mock/mock_handler.go --package=mock {{.Module}}/internal/handler {{.Service}}er -type {{.Service}}er interface { - Handler -} - -type {{.Service}} struct { -} - -func New{{.Service}}() {{.Service}}er { - return &{{.Service}}{} -} - -func (m {{.Service}}) MethodNotAllowed(w http.ResponseWriter, _ *http.Request) { - response.ToJson(w, http.StatusMethodNotAllowed, http.StatusText(http.StatusMethodNotAllowed), nil) -} - -func (m {{.Service}}) RouteNotFound(w http.ResponseWriter, _ *http.Request) { - response.ToJson(w, http.StatusNotFound, http.StatusText(http.StatusNotFound), nil) -} - -func (m {{.Service}}) HealthCheck(w http.ResponseWriter, _ *http.Request) { - response.ToJson(w, http.StatusOK, http.StatusText(http.StatusOK), nil) -} diff --git a/cmd/gosvc/resources/internal/router/router.go.tmpl b/cmd/gosvc/resources/internal/router/router.go.tmpl deleted file mode 100644 index e614517..0000000 --- a/cmd/gosvc/resources/internal/router/router.go.tmpl +++ /dev/null @@ -1,27 +0,0 @@ -package router - -import ( - "net/http" - - "github.com/PereRohit/util/constant" - "github.com/PereRohit/util/middleware" - "github.com/gorilla/mux" - - "{{.Module}}/internal/handler" -) - -func Register() *mux.Router { - m := mux.NewRouter() - - m.StrictSlash(true) - m.Use(middleware.RequestHijacker) - m.Use(middleware.RecoverPanic) - - svc := handler.New{{.Service}}() - - m.HandleFunc(constant.HealthRoute, svc.HealthCheck) - m.NotFoundHandler = http.HandlerFunc(svc.RouteNotFound) - m.MethodNotAllowedHandler = http.HandlerFunc(svc.MethodNotAllowed) - - return m -} diff --git a/internal/resources.go b/internal/resources.go new file mode 100644 index 0000000..616406e --- /dev/null +++ b/internal/resources.go @@ -0,0 +1,12 @@ +package internal + +import "embed" + +var ( + //go:embed resources + f embed.FS +) + +func GetEmbeddedFS() embed.FS { + return f +} diff --git a/internal/resources/README.md.tmpl b/internal/resources/README.md.tmpl new file mode 100644 index 0000000..2b9dd67 --- /dev/null +++ b/internal/resources/README.md.tmpl @@ -0,0 +1,3 @@ +# {{.Service}} + +##### Generated with [gosvc cli](https://github.com/PereRohit/gosvc) \ No newline at end of file diff --git a/cmd/gosvc/resources/cmd/service/main.go.tmpl b/internal/resources/cmd/service/main.go.tmpl similarity index 100% rename from cmd/gosvc/resources/cmd/service/main.go.tmpl rename to internal/resources/cmd/service/main.go.tmpl diff --git a/cmd/gosvc/resources/go.mod.tmpl b/internal/resources/go.mod.tmpl similarity index 100% rename from cmd/gosvc/resources/go.mod.tmpl rename to internal/resources/go.mod.tmpl diff --git a/internal/resources/internal/handler/common.go.tmpl b/internal/resources/internal/handler/common.go.tmpl new file mode 100644 index 0000000..f9b713b --- /dev/null +++ b/internal/resources/internal/handler/common.go.tmpl @@ -0,0 +1,67 @@ +package handler + +import ( + "net/http" + "sync" + + "github.com/PereRohit/util/response" +) + +//go:generate mockgen --build_flags=--mod=mod --destination=./../../pkg/mock/mock_common.go --package=mock {{.Module}}/internal/handler Commoner,HealthChecker + +type Commoner interface { + MethodNotAllowed(http.ResponseWriter, *http.Request) + RouteNotFound(http.ResponseWriter, *http.Request) + HealthCheck(http.ResponseWriter, *http.Request) +} + +type HealthChecker interface { + HealthCheck() (svcName string, msg string, stat bool) +} + +type common struct { + sync.Mutex + services []HealthChecker +} + +var c = common{} + +func AddHealthChecker(h HealthChecker) { + c.Lock() + defer c.Unlock() + c.services = append(c.services, h) +} + +func NewCommonSvc() Commoner { + return &c +} + +func (common) MethodNotAllowed(w http.ResponseWriter, _ *http.Request) { + response.ToJson(w, http.StatusMethodNotAllowed, http.StatusText(http.StatusMethodNotAllowed), nil) +} + +func (common) RouteNotFound(w http.ResponseWriter, _ *http.Request) { + response.ToJson(w, http.StatusNotFound, http.StatusText(http.StatusNotFound), nil) +} + +func (common) HealthCheck(w http.ResponseWriter, _ *http.Request) { + type svcHealthStat struct { + Status string `json:"status"` + Message string `json:"message,omitempty"` + } + + svcUpdate := map[string]svcHealthStat{} + + for _, svc := range c.services { + name, msg, ok := svc.HealthCheck() + stat := http.StatusText(http.StatusOK) + if !ok { + stat = "Not " + stat + } + svcUpdate[name] = svcHealthStat{ + Status: stat, + Message: msg, + } + } + response.ToJson(w, http.StatusOK, http.StatusText(http.StatusOK), svcUpdate) +} diff --git a/internal/resources/internal/handler/handler.go.tmpl b/internal/resources/internal/handler/handler.go.tmpl new file mode 100644 index 0000000..939e5f4 --- /dev/null +++ b/internal/resources/internal/handler/handler.go.tmpl @@ -0,0 +1,67 @@ +package handler + +import ( + "fmt" + "net/http" + + "github.com/PereRohit/util/request" + "github.com/PereRohit/util/response" + + "{{.Module}}/internal/logic" + "{{.Module}}/internal/model" +) + +const {{.Service}}Name = "{{.Service}}" + +var ( + svc = &{{.Service}}{} +) + +//go:generate mockgen --build_flags=--mod=mod --destination=./../../pkg/mock/mock_handler.go --package=mock {{.Module}}/internal/handler {{.Service}}Handler + +type {{.Service}}Handler interface { + HealthChecker + Ping(w http.ResponseWriter, r *http.Request) +} + +type {{.Service}} struct { + logic logic.{{.Service}}LogicIer +} + +func init() { + AddHealthChecker(svc) +} + +func New{{.Service}}() {{.Service}}Handler { + svc = &{{.Service}}{ + logic: logic.New{{.Service}}Logic(), + } + + return svc +} + +func (svc {{.Service}}) HealthCheck() (svcName string, msg string, stat bool) { + set := false + defer func() { + svcName = {{.Service}}Name + if !set { + msg = "" + stat = true + } + }() + return +} + +func (svc {{.Service}}) Ping(w http.ResponseWriter, r *http.Request) { + req := &model.PingRequest{} + + suggestedCode, err := request.FromJson(r, req) + if err != nil { + response.ToJson(w, suggestedCode, fmt.Sprintf("FAILED: %s", err.Error()), nil) + return + } + // call logic + resp := svc.logic.Ping(req) + response.ToJson(w, resp.Status, resp.Message, resp.Data) + return +} diff --git a/internal/resources/internal/logic/logic.go.tmpl b/internal/resources/internal/logic/logic.go.tmpl new file mode 100644 index 0000000..c787192 --- /dev/null +++ b/internal/resources/internal/logic/logic.go.tmpl @@ -0,0 +1,26 @@ +package logic + +import ( + "net/http" + + respModel "github.com/PereRohit/util/model" + "{{.Module}}/internal/model" +) + +type {{.Service}}LogicIer interface { + Ping(*model.PingRequest) *respModel.Response +} + +type {{.Service}}Logic struct{} + +func New{{.Service}}Logic() {{.Service}}LogicIer { + return &{{.Service}}Logic{} +} + +func (l {{.Service}}Logic) Ping(req *model.PingRequest) *respModel.Response { + return &respModel.Response{ + Status: http.StatusOK, + Message: "Pong", + Data: req.Data, + } +} diff --git a/internal/resources/internal/logic/mockgen.go.tmpl b/internal/resources/internal/logic/mockgen.go.tmpl new file mode 100644 index 0000000..fef13a6 --- /dev/null +++ b/internal/resources/internal/logic/mockgen.go.tmpl @@ -0,0 +1,4 @@ +package logic + +//go:generate mockgen --build_flags=--mod=mod --destination=./../../pkg/mock/mock_logic.go --package=mock {{.Module}}/internal/logic {{.Service}}LogicIer + diff --git a/internal/resources/internal/model/model.go.tmpl b/internal/resources/internal/model/model.go.tmpl new file mode 100644 index 0000000..f9c64ec --- /dev/null +++ b/internal/resources/internal/model/model.go.tmpl @@ -0,0 +1,5 @@ +package model + +type PingRequest struct { + Data any `json:"data" validate:"required"` +} diff --git a/internal/resources/internal/router/router.go.tmpl b/internal/resources/internal/router/router.go.tmpl new file mode 100644 index 0000000..eaa03f1 --- /dev/null +++ b/internal/resources/internal/router/router.go.tmpl @@ -0,0 +1,37 @@ +package router + +import ( + "net/http" + + "github.com/PereRohit/util/constant" + "github.com/PereRohit/util/middleware" + "github.com/gorilla/mux" + + "{{.Module}}/internal/handler" +) + +func Register() *mux.Router { + m := mux.NewRouter() + + m.StrictSlash(true) + m.Use(middleware.RequestHijacker) + m.Use(middleware.RecoverPanic) + + commons := handler.NewCommonSvc() + m.HandleFunc(constant.HealthRoute, commons.HealthCheck).Methods(http.MethodGet) + m.NotFoundHandler = http.HandlerFunc(commons.RouteNotFound) + m.MethodNotAllowedHandler = http.HandlerFunc(commons.MethodNotAllowed) + + // attach routes for services below + m = attach{{.Service}}Routes(m) + + return m +} + +func attach{{.Service}}Routes(m *mux.Router) *mux.Router { + svc := handler.New{{.Service}}() + + m.HandleFunc("/ping", svc.Ping).Methods(http.MethodPost) + + return m +}