diff --git a/cmd/main.go b/cmd/main.go index 947cd10..1a5b091 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -12,6 +12,7 @@ import ( "time" "github.com/joshsoftware/code-curiosity-2025/internal/app" + "github.com/joshsoftware/code-curiosity-2025/internal/app/cronJob" "github.com/joshsoftware/code-curiosity-2025/internal/config" ) @@ -43,6 +44,9 @@ func main() { router := app.NewRouter(dependencies) + newCronSchedular := cronJob.NewCronSchedular() + newCronSchedular.InitCronJobs(dependencies.ContributionService) + server := http.Server{ Addr: fmt.Sprintf(":%s", cfg.HTTPServer.Port), Handler: router, diff --git a/go.mod b/go.mod index f308cae..e73f2e2 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/ilyakaznacheev/cleanenv v1.5.0 github.com/jmoiron/sqlx v1.4.0 github.com/lib/pq v1.10.9 + github.com/robfig/cron/v3 v3.0.1 golang.org/x/oauth2 v0.29.0 google.golang.org/api v0.231.0 ) diff --git a/go.sum b/go.sum index d272ace..a599248 100644 --- a/go.sum +++ b/go.sum @@ -133,6 +133,8 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgm github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= 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/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE= diff --git a/internal/app/contribution/handler.go b/internal/app/contribution/handler.go index 19b9d2e..df50396 100644 --- a/internal/app/contribution/handler.go +++ b/internal/app/contribution/handler.go @@ -13,7 +13,6 @@ type handler struct { } type Handler interface { - FetchUserLatestContributions(w http.ResponseWriter, r *http.Request) FetchUsersAllContributions(w http.ResponseWriter, r *http.Request) } @@ -23,20 +22,6 @@ func NewHandler(contributionService Service) Handler { } } -func (h *handler) FetchUserLatestContributions(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - err := h.contributionService.ProcessFetchedContributions(ctx) - if err != nil { - slog.Error("error fetching latest contributions") - status, errorMessage := apperrors.MapError(err) - response.WriteJson(w, status, errorMessage, nil) - return - } - - response.WriteJson(w, http.StatusOK, "contribution fetched successfully", nil) -} - func (h *handler) FetchUsersAllContributions(w http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/internal/app/cronJob/cronjob.go b/internal/app/cronJob/cronjob.go new file mode 100644 index 0000000..f8f3a21 --- /dev/null +++ b/internal/app/cronJob/cronjob.go @@ -0,0 +1,24 @@ +package cronJob + +import ( + "context" + "log/slog" + "time" +) + +type Job interface { + Schedule(c *CronSchedular) error +} + +type CronJob struct { + Name string +} + +func (c *CronJob) Execute(ctx context.Context, fn func(context.Context)) { + slog.Info("cron job started at", "time ", time.Now()) + defer func() { + slog.Info("cron job completed") + }() + + fn(ctx) +} diff --git a/internal/app/cronJob/dailyJob.go b/internal/app/cronJob/dailyJob.go new file mode 100644 index 0000000..67f3e24 --- /dev/null +++ b/internal/app/cronJob/dailyJob.go @@ -0,0 +1,32 @@ +package cronJob + +import ( + "context" + + "github.com/joshsoftware/code-curiosity-2025/internal/app/contribution" +) + +type DailyJob struct { + CronJob + contributionService contribution.Service +} + +func NewDailyJob(contributionService contribution.Service) *DailyJob { + return &DailyJob{ + contributionService: contributionService, + CronJob: CronJob{Name: "Fetch Contributions Daily"}, + } +} + +func (d *DailyJob) Schedule(s *CronSchedular) error { + _, err := s.cron.AddFunc("0 1 * * *", func() { d.Execute(context.Background(), d.run) }) + if err != nil { + return err + } + + return nil +} + +func (d *DailyJob) run(ctx context.Context) { + d.contributionService.ProcessFetchedContributions(ctx) +} diff --git a/internal/app/cronJob/init.go b/internal/app/cronJob/init.go new file mode 100644 index 0000000..7fba0f3 --- /dev/null +++ b/internal/app/cronJob/init.go @@ -0,0 +1,38 @@ +package cronJob + +import ( + "log/slog" + "time" + + "github.com/joshsoftware/code-curiosity-2025/internal/app/contribution" + "github.com/robfig/cron/v3" +) + +type CronSchedular struct { + cron *cron.Cron +} + +func NewCronSchedular() *CronSchedular { + location, err := time.LoadLocation("Asia/Kolkata") + if err != nil { + slog.Error("failed to load IST timezone", "error", err) + } + + return &CronSchedular{ + cron: cron.New(cron.WithLocation(location)), + } +} + +func (c *CronSchedular) InitCronJobs(contributionService contribution.Service) { + jobs := []Job{ + NewDailyJob(contributionService), + } + + for _, job := range jobs { + if err := job.Schedule(c); err != nil { + slog.Error("failed to execute cron job") + } + } + + c.cron.Start() +} diff --git a/internal/app/dependencies.go b/internal/app/dependencies.go index 616e377..e65fd39 100644 --- a/internal/app/dependencies.go +++ b/internal/app/dependencies.go @@ -15,8 +15,7 @@ import ( ) type Dependencies struct { - AuthService auth.Service - UserService user.Service + ContributionService contribution.Service AuthHandler auth.Handler UserHandler user.Handler ContributionHandler contribution.Handler @@ -40,8 +39,7 @@ func InitDependencies(db *sqlx.DB, appCfg config.AppConfig, client config.Bigque contributionHandler := contribution.NewHandler(contributionService) return Dependencies{ - AuthService: authService, - UserService: userService, + ContributionService: contributionService, AuthHandler: authHandler, UserHandler: userHandler, ContributionHandler: contributionHandler, diff --git a/internal/app/router.go b/internal/app/router.go index 6ab919a..7c15d9b 100644 --- a/internal/app/router.go +++ b/internal/app/router.go @@ -20,7 +20,6 @@ func NewRouter(deps Dependencies) http.Handler { router.HandleFunc("PATCH /api/v1/user/email", middleware.Authentication(deps.UserHandler.UpdateUserEmail, deps.AppCfg)) - router.HandleFunc("GET /api/v1/user/contributions/latest", middleware.Authentication(deps.ContributionHandler.FetchUserLatestContributions, deps.AppCfg)) router.HandleFunc("GET /api/v1/user/contributions/all", middleware.Authentication(deps.ContributionHandler.FetchUsersAllContributions, deps.AppCfg)) return middleware.CorsMiddleware(router, deps.AppCfg) }