jobs-monorepo/services/api/pkg/http/job.go
Elshimy Ziad Magdy Taha 8a4ad07e46 fix swagger
2025-12-18 14:02:13 +05:00

209 lines
7.4 KiB
Go

package http
import (
"encoding/json"
"fmt"
"log/slog"
"net/http"
"strconv"
"github.com/gorilla/mux"
"github.com/gorilla/schema"
"github.com/jobs-scraper/internal/pkg/utils"
"github.com/jobs-scraper/libs/ports"
"github.com/jobs-scraper/libs/repo"
)
// JobHandler handles HTTP requests related to jobs
type JobHandler struct {
jobCommands *ports.JobCommands
jobAnalysisResultRepo *repo.JobAnalysisResultRepository
jobQueries *ports.JobQueries
}
// NewJobHandler creates a new instance of JobHandler
func NewJobHandler(jobCommands *ports.JobCommands, jobAnalysisResultRepo *repo.JobAnalysisResultRepository, jobQueries *ports.JobQueries) *JobHandler {
return &JobHandler{
jobCommands: jobCommands,
jobAnalysisResultRepo: jobAnalysisResultRepo,
jobQueries: jobQueries,
}
}
var decoder = schema.NewDecoder()
// RegisterRoutes registers all routes to the router
func (h *JobHandler) RegisterRoutes(router *mux.Router) {
jobs := router.PathPrefix("/jobs").Subrouter()
//jobs.Use(AuthMiddleware)
jobs.HandleFunc("", utils.Make(h.CreateJob)).Methods("POST")
jobs.HandleFunc("/analysis", utils.Make(h.GetAllAnalysisResults)).Methods("GET")
jobs.HandleFunc("/{id}/analysis", utils.Make(h.GetJobAnalysisResult)).Methods("GET")
jobs.HandleFunc("/analysis/top-matches", utils.Make(h.GetTopMatches)).Methods("GET")
jobs.HandleFunc("", utils.Make(h.GetJobs)).Methods("GET")
}
// CreateJob handles the creation of a new job
// @Summary Create a new job
// @Description Creates a new job with the provided specifications
// @Tags jobs
// @Accept json
// @Produce json
// @Param job body ports.CreateJobCommand true "Job creation data"
// @Success 201 {object} map[string]string "Successfully created job"
// @Failure 400 {object} map[string]string "Invalid request data"
// @Failure 500 {object} map[string]string "Internal server error"
// @Router /jobs [post]
func (h *JobHandler) CreateJob(w http.ResponseWriter, r *http.Request) error {
var cmd ports.CreateJobCommand
if err := json.NewDecoder(r.Body).Decode(&cmd); err != nil {
slog.Error(err.Error())
return utils.NewAPIError(http.StatusBadRequest, utils.InvalidJSON())
}
err := h.jobCommands.CreateJob.Handle(cmd)
if err != nil {
slog.Error(err.Error())
return utils.NewAPIError(http.StatusInternalServerError, err)
}
utils.WriteJSON(w, http.StatusCreated, map[string]string{"message": "Success"})
return nil
}
// GetJobs handles the retrieval of all jobs
// @Summary Gets all jobs
// @Description Gets all jobs with the provided specifications
// @Tags jobs
// @Param location query string false "Location filter"
// @Param keywords query string false "Keywords filter (searches title and company)"
// @Param fwt query string false "Full/Part time filter"
// @Param provider query int false "Job provider (LinkedIn=0, Indeed=1, Glassdoor=2, Bayt=3, TokyoDev=4, JapanDev=5, Google=6, HiringCafe=7)" Enums(0,1,2,3,4,5,6,7)
// @Param pageNumber query int false "Page number"
// @Param pageSize query int false "Number of elements"
// @Accept json
// @Produce json
// @Success 200 {object} map[string]any "Successfully retrieved jobs"
// @Failure 400 {object} map[string]any "Invalid request data"
// @Failure 500 {object} map[string]any "Internal server error"
// @Router /jobs [get]
func (h *JobHandler) GetJobs(w http.ResponseWriter, r *http.Request) error {
query := ports.GetJobQuery{
PageNumber: 1,
PageSize: 10,
}
if pageNumberStr := r.URL.Query().Get("pageNumber"); pageNumberStr != "" {
if page, err := strconv.Atoi(pageNumberStr); err == nil && page > 0 {
query.PageNumber = page
}
}
if pageSizeStr := r.URL.Query().Get("pageSize"); pageSizeStr != "" {
if pageSize, err := strconv.Atoi(pageSizeStr); err == nil && pageSize > 0 {
query.PageSize = pageSize
}
}
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
slog.Error("Failed to decode query params", "error", err)
return utils.NewAPIError(http.StatusBadRequest, fmt.Errorf("invalid query parameters: %w", err))
}
result, totalCount, err := h.jobQueries.GetJobs.Handle(query)
if err != nil {
slog.Error(err.Error())
return utils.NewAPIError(http.StatusInternalServerError, err)
}
pagination := utils.NewPageViewModel(totalCount, query.PageNumber, query.PageSize)
utils.WriteJSON(w, http.StatusOK, map[string]any{
"jobs": result,
"pagination": pagination,
})
return nil
}
// GetAllAnalysisResults handles retrieving all job analysis results
// @Summary Get all job analysis results
// @Description Retrieves all job analysis results from the database
// @Tags jobs
// @Produce json
// @Success 200 {array} domain.JobAnalysisResult "List of job analysis results"
// @Failure 500 {object} map[string]string "Internal server error"
// @Router /jobs/analysis [get]
func (h *JobHandler) GetAllAnalysisResults(w http.ResponseWriter, r *http.Request) error {
results, err := h.jobAnalysisResultRepo.GetAllAnalysisResults()
if err != nil {
slog.Error("Failed to get analysis results", "error", err)
return utils.NewAPIError(http.StatusInternalServerError, err)
}
utils.WriteJSON(w, http.StatusOK, results)
return nil
}
// GetJobAnalysisResult handles retrieving analysis result for a specific job
// @Summary Get job analysis result by job ID
// @Description Retrieves the analysis result for a specific job
// @Tags jobs
// @Produce json
// @Param id path int true "Job ID"
// @Success 200 {object} domain.JobAnalysisResult "Job analysis result"
// @Failure 400 {object} map[string]string "Invalid job ID"
// @Failure 404 {object} map[string]string "Analysis result not found"
// @Failure 500 {object} map[string]string "Internal server error"
// @Router /jobs/{id}/analysis [get]
func (h *JobHandler) GetJobAnalysisResult(w http.ResponseWriter, r *http.Request) error {
vars := mux.Vars(r)
jobID, err := strconv.ParseInt(vars["id"], 10, 64)
if err != nil {
return utils.NewAPIError(http.StatusBadRequest, fmt.Errorf("invalid job ID"))
}
result, err := h.jobAnalysisResultRepo.GetAnalysisResultByJobID(jobID)
if err != nil {
slog.Error("Failed to get analysis result", "jobID", jobID, "error", err)
return utils.NewAPIError(http.StatusNotFound, fmt.Errorf("analysis result not found"))
}
utils.WriteJSON(w, http.StatusOK, result)
return nil
}
// GetTopMatches handles retrieving top matching jobs based on analysis score
// @Summary Get top matching jobs
// @Description Retrieves jobs with high analysis match scores
// @Tags jobs
// @Produce json
// @Param min_score query int false "Minimum match score (default: 70)"
// @Success 200 {array} domain.JobAnalysisResult "List of top matching job analysis results"
// @Failure 400 {object} map[string]string "Invalid minimum score"
// @Failure 500 {object} map[string]string "Internal server error"
// @Router /jobs/analysis/top-matches [get]
func (h *JobHandler) GetTopMatches(w http.ResponseWriter, r *http.Request) error {
minScoreStr := r.URL.Query().Get("min_score")
minScore := 70 // default minimum score
if minScoreStr != "" {
var err error
minScore, err = strconv.Atoi(minScoreStr)
if err != nil {
return utils.NewAPIError(http.StatusBadRequest, fmt.Errorf("invalid minimum score"))
}
}
results, err := h.jobAnalysisResultRepo.GetAnalysisResultsByMatchScore(minScore)
if err != nil {
slog.Error("Failed to get top matches", "minScore", minScore, "error", err)
return utils.NewAPIError(http.StatusInternalServerError, err)
}
utils.WriteJSON(w, http.StatusOK, results)
return nil
}