jobs-monorepo/internal/repo/job-description.go
Elshimy Ziad Magdy Taha 2ab810be72
All checks were successful
Deploy scraper-google / build (push) Successful in 21m58s
Deploy scraper-google / deploy (push) Has been skipped
Move shared Go packages from libs/ to internal/
internal/ and libs/ were both shared-code roots with no rule for which
one a package belonged in, so the split carried no information. Adopt a
rule the language answers by itself:

  shared Go          -> internal/
  shared TypeScript  -> libs/   (npm workspace packages)

libs/{ports,repo,server} move to internal/, leaving libs/ holding only
the two TypeScript packages (@jobs-scraper/rabbitmq-ts and
@jobs-scraper/browser-automation), which matches npm workspace
convention. internal/ is also Go's marker for code not importable from
outside the repo, which is accurate here since none of it is published.

Import paths are rewritten mechanically (jobs-scraper/libs/ ->
jobs-scraper/internal/) across 15 lines in 10 files. The 6 moved files
are pure renames with no content change. Doing this after the module
collapse in the previous commit meant no go.mod or replace-directive
edits were needed.

gofmt is applied to services/api/pkg/http/job.go, whose import group the
rewrite left out of order. Three files were already unformatted before
this refactor (internal/openai/interface.go, internal/ports/job-queries.go,
internal/repo/job-analysis-result.go) and are deliberately left alone to
keep this diff limited to the move.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 17:30:04 +05:00

87 lines
2.3 KiB
Go

package repo
import (
"database/sql"
"encoding/json"
"fmt"
"strings"
"github.com/jobs-scraper/internal/domain"
)
type JobDescriptionRepository struct {
db *sql.DB
}
type JobDescriptionData struct {
JobID int
Description string
Criteria map[string]string
}
func NewJobDescriptionRepository(db *sql.DB) *JobDescriptionRepository {
return &JobDescriptionRepository{db: db}
}
func (r *JobDescriptionRepository) SaveJobDescriptions(jobDescriptions []domain.JobDescription) error {
if len(jobDescriptions) == 0 {
return nil
}
// Build the VALUES clause dynamically
valueStrings := make([]string, 0, len(jobDescriptions))
valueArgs := make([]interface{}, 0, len(jobDescriptions)*3)
for i, jd := range jobDescriptions {
// Convert criteria map to JSONB
criteriaByte, err := json.Marshal(jd.Criteria)
if err != nil {
return fmt.Errorf("error marshaling job criteria for job %d: %v", jd.JobID, err)
}
valueStrings = append(valueStrings, fmt.Sprintf("($%d, $%d, $%d)", i*3+1, i*3+2, i*3+3))
valueArgs = append(valueArgs, jd.JobID, jd.Description, criteriaByte)
}
sqlStatement := fmt.Sprintf(`
INSERT INTO job_descriptions (job_id, description, job_criteria)
VALUES %s
ON CONFLICT (job_id) DO UPDATE SET
description = EXCLUDED.description,
job_criteria = EXCLUDED.job_criteria,
updated_at = CURRENT_TIMESTAMP
`, strings.Join(valueStrings, ","))
_, err := r.db.Exec(sqlStatement, valueArgs...)
if err != nil {
return fmt.Errorf("error saving job descriptions: %v", err)
}
return nil
}
func (r *JobDescriptionRepository) GetJobDescriptionByJobID(jobID int64) (string, map[string]string, error) {
var (
description string
criteriaByte []byte
criteria map[string]string
)
sqlStatement := `SELECT description, job_criteria FROM job_descriptions WHERE job_id = $1`
err := r.db.QueryRow(sqlStatement, jobID).Scan(&description, &criteriaByte)
if err != nil {
if err == sql.ErrNoRows {
return "", nil, nil // No description found
}
return "", nil, fmt.Errorf("error fetching job description: %v", err)
}
// Unmarshal the JSON criteria
if criteriaByte != nil {
if err := json.Unmarshal(criteriaByte, &criteria); err != nil {
return "", nil, fmt.Errorf("error unmarshaling job criteria: %v", err)
}
}
return description, criteria, nil
}