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 +}