Skip to main content

[Employer Sync 3.1] Skeleton: EmployersSyncService with stubbed mappers

TL;DR: Empty shell of EmployersSyncServicefind_in_batches(1000), per-record begin/rescue, JodGig::TimezoneService watermark, Gig::SyncLog write — with every mapping function stubbed to raise UnimplementedError. Each real mapper lands as its own small PR (4.1–4.4).

Context

The employer sync has the same shape as the existing talent sync: a batch job, an incremental watermark on updated_at, per-record error handling, a Gig::SyncLog row at the end. We want that shell in place first, with every business decision stubbed out — so each mapper can land as its own small, reviewable PR.

Problem

PR #1634 put service, job, mappers, dedup, and bug-fixes all into one 583-line change. It was hard to review with a mid-level engineer. The redo splits them.

Direction

Build only the shell. No real business logic in this PR.

ServiceJodGig::Users::EmployersSyncService.execute:

  • Read the last successful Gig::SyncLog for this origin/destination pair.
  • Compute the incremental watermark through JodGig::TimezoneService (the service that branch 1642 introduces — coordinate). Never + 8.hours.
  • Call select_employers_in_scope (stubbed — returns an empty relation).
  • find_in_batches(batch_size: 1000) — same size as the talent sync.
  • For each record in a batch, run inside its own begin / rescue — append failures to fail_log, report to Sentry with the user id, continue. Per-record, not per-batch.
  • Inside the per-record block: call map_to_identities_user, upsert_identities_users (single-row), then map_to_org_memberships, upsert_org_memberships, then map_to_org_outlet_assignments, upsert_org_outlet_assignments. All stubbed for now.
  • End by writing a Gig::SyncLog row with is_successful = fail_log.blank?.

JobJodGig::Users::EmployersSyncJob:

  • Sidekiq::Job; sidekiq_options(queue: :low, retry: 0, dead: true, backtrace: 10).
  • One line: JodGig::Users::EmployersSyncService.execute().

Scheduler — add the entry in config/sidekiq_scheduler.yml. Cadence is set by issue 5.3 (hourly); for now register it with a cron placeholder noting "set in 5.3."

Stubs. Each of the methods below lives on the service and raises Errors::UnimplementedError with the issue reference:

  • select_employers_in_scope → "implemented in 4.1"
  • map_to_identities_user(jodgig_user) → "implemented in 4.2"
  • upsert_identities_users(rows) → "implemented in 4.2"
  • map_to_org_memberships(...) → "implemented in 4.3"
  • upsert_org_memberships(rows) → "implemented in 4.3"
  • map_to_org_outlet_assignments(...) → "implemented in 4.4"
  • upsert_org_outlet_assignments(rows) → "implemented in 4.4"
  • resolve_owner_per_company(...) → "implemented in 4.3"

Gig::SyncLog enum — add jodgig_users_employers to origin_table.

Acceptance

  • Service, job, and scheduler entry exist; follow .ai/instructions.md patterns.
  • Per-record begin / rescue is wired in the batch loop (not per-batch).
  • Stubs raise UnimplementedError with the issue reference in the message.
  • The sync runs in QA against an empty scope and writes a clean Gig::SyncLog row (is_successful: true).

Depends on

  • 2.1, 2.2 (schema must exist before the sync touches it)