Skip to content

Commit

Permalink
feat: added job scheduler support
Browse files Browse the repository at this point in the history
  • Loading branch information
hokamsingh committed Aug 19, 2024
1 parent ca63a11 commit d5aecc5
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 23 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.22.5

require (
github.com/gorilla/mux v1.8.1
github.com/lpernett/godotenv v0.0.0-20230527005122-0de1d4c5ef5e
github.com/joho/godotenv v1.5.1
github.com/robfig/cron/v3 v3.0.1
go.uber.org/dig v1.18.0
)
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/lpernett/godotenv v0.0.0-20230527005122-0de1d4c5ef5e h1:6b4YTtccT1y/3eSsDCVhB6boPPCh5bQwP1Pa863yH28=
github.com/lpernett/godotenv v0.0.0-20230527005122-0de1d4c5ef5e/go.mod h1:K+inF/XYdmRn4sSP3IU4EM3KcOdGVJUJqZPmrQSxjGo=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
go.uber.org/dig v1.18.0 h1:imUL1UiY0Mg4bqbFfsRQO5G4CGRBec/ZujWTvSVp3pw=
Expand Down
28 changes: 15 additions & 13 deletions internal/core/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,32 @@ package config
import (
"log"
"os"
"strings"

"github.com/lpernett/godotenv"
"github.com/joho/godotenv"
)

type Config struct {
ServerPort string
Env string
JwtSecret string
}
type Config map[string]string

func LoadConfig() *Config {
func LoadConfig() Config {
if err := godotenv.Load(); err != nil {
log.Printf("No .env file found: %v", err)
}

return &Config{
ServerPort: getEnv("SERVER_PORT", "8080"),
Env: getEnv("ENV", "development"),
JwtSecret: getEnv("JWT_SECRET", "secret"),
config := make(Config)

for _, env := range os.Environ() {
pair := strings.SplitN(env, "=", 2)
if len(pair) == 2 {
config[pair[0]] = pair[1]
}
}

return config
}

func getEnv(key, defaultValue string) string {
if value, exists := os.LookupEnv(key); exists {
func (c Config) Get(key, defaultValue string) string {
if value, exists := c[key]; exists {
return value
}
return defaultValue
Expand Down
70 changes: 70 additions & 0 deletions internal/core/di/di.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package di

import (
"log"

scheduler "github.com/hokamsingh/lessgo/internal/core/job"
"go.uber.org/dig"
)

Expand All @@ -26,3 +29,70 @@ func (c *Container) Provide(constructor interface{}) error {
func (c *Container) Invoke(function interface{}) error {
return c.container.Invoke(function)
}

/*
RegisterScheduler sets up and registers the scheduler in the DI container.
This method ensures that the scheduler is available for dependency injection within your LessGo application. It uses the `cron` package under the hood to provide scheduling capabilities.
### Example Usage
```go
package main
import (
"log"
"github.com/hokamsingh/lessgo/pkg/lessgo"
"github.com/hokamsingh/lessgo/pkg/lessgo/scheduler"
)
func main() {
// Create a new DI container
container := lessgo.NewContainer()
// Register the scheduler in the container
if err := container.RegisterScheduler(); err != nil {
log.Fatalf("Error registering scheduler: %v", err)
}
// Use the scheduler
err := container.InvokeScheduler(func(sched scheduler.Scheduler) error {
// Add a job to the scheduler
if err := sched.AddJob("@every 1m", func() {
log.Println("Job running every minute")
}); err != nil {
return err
}
// Start the scheduler
sched.Start()
// Optionally, stop the scheduler when your application shuts down
defer sched.Stop()
return nil
})
if err != nil {
log.Fatalf("Error invoking scheduler: %v", err)
}
// Start your application logic here
}
*/
func (c *Container) RegisterScheduler() error {
sched := scheduler.NewCronScheduler()
return c.Register(func() scheduler.Scheduler {
return sched
})
}

// InvokeScheduler provides access to the scheduler for initialization or configuration
func (c *Container) InvokeScheduler(fn func(scheduler.Scheduler) error) error {
return c.container.Invoke(func(sched scheduler.Scheduler) {
if err := fn(sched); err != nil {
log.Fatalf("Error invoking scheduler: %v", err)
}
})
}
37 changes: 37 additions & 0 deletions internal/core/job/scheduler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package scheduler

import (
"github.com/robfig/cron/v3"
)

type Scheduler interface {
AddJob(schedule string, job func()) error
Start()
Stop()
}

type CronScheduler struct {
cron *cron.Cron
}

func NewCronScheduler() *CronScheduler {
return &CronScheduler{
cron: cron.New(),
}
}

func (s *CronScheduler) AddJob(schedule string, job func()) error {
_, err := s.cron.AddFunc(schedule, job)
if err != nil {
return err
}
return nil
}

func (s *CronScheduler) Start() {
s.cron.Start()
}

func (s *CronScheduler) Stop() {
s.cron.Stop()
}
54 changes: 48 additions & 6 deletions internal/core/router/router.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package router

import (
"fmt"
"log"
"net/http"
"path/filepath"
Expand Down Expand Up @@ -104,17 +105,58 @@ func (r *Router) Start(addr string) error {
return http.ListenAndServe(addr, finalHandler)
}

// withErrorHandling wraps the given handler with error handling middleware
// HTTPError represents an error with an associated HTTP status code.
type HTTPError struct {
Code int
Message string
}

func (e *HTTPError) Error() string {
return fmt.Sprintf("%d - %s", e.Code, e.Message)
}

// NewHTTPError creates a new HTTPError instance.
func NewHTTPError(code int, message string) *HTTPError {
return &HTTPError{
Code: code,
Message: message,
}
}

/*
withErrorHandling wraps the given HTTP handler function with centralized error handling.
This middleware intercepts any panics that occur during the execution of the handler function,
and handles them based on their type. If the panic is of type *HTTPError, it sends an HTTP response
with the specified status code and message. For other types of panics, it logs the error and stack trace,
and sends a generic "Internal Server Error" response.
Example:
handler := r.withErrorHandling(func(w http.ResponseWriter, r *http.Request) {
// Example: Trigger a Bad Request error
panic(LessGo.NewHTTPError(http.StatusBadRequest, "Bad Request: missing parameters"))
})
// Use the handler in your router
r.Mux.Handle("/example", handler)
*/
func (r *Router) withErrorHandling(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, req *http.Request) {
defer func() {
if err := recover(); err != nil {
log.Printf("An error occurred: %v", err)
log.Printf("Stack trace:\n%s\n", debug.Stack())
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
switch e := err.(type) {
case *HTTPError:
log.Printf("HTTP error occurred: %v", e)
http.Error(w, e.Message, e.Code)
default:
log.Printf("An unexpected error occurred: %v", err)
log.Printf("Stack trace:\n%s\n", debug.Stack())
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}
}()
next(w, r)
next(w, req)
}
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/lessgo/less.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type FileUploadMiddleware = middleware.FileUploadMiddleware
// LoadConfig loads the configuration
func LoadConfig() config.Config {
config := config.LoadConfig()
return *config
return config
}

// NewContainer creates a new dependency injection container
Expand Down

0 comments on commit d5aecc5

Please sign in to comment.