Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / internal/modules/recruitmatch/service.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | internal/modules/recruitmatch/service.go |
| Original SHA-256 | bac429a19afaff71e6d35236a106c8c2bfcc1d28c76a9dc65e15de05414cc77f |
| Git blob SHA | c6acdb823a85e3cef5eb4d866f8472f0cb1d9e35 |
| Reading-copy SHA-256 | bac429a19afaff71e6d35236a106c8c2bfcc1d28c76a9dc65e15de05414cc77f |
Every recorded occurrence
These 2 records contain identical original source bytes. Their paths and revisions remain part of the archive.
| Original record | Branch or archive | Revision / identifier | Original location |
|---|---|---|---|
| Janady13/missionmatch / internal/modules/recruitmatch/service.go | feature/opportunity-to-outcome | 720d47fae476b0d38477ae4401697d372b1ea2bc | Open original |
| Janady13/missionmatch / internal/modules/recruitmatch/service.go | feature/aioe-upgrade | 2178e7298d0e5d9af83afa12402fd587f0ba6ed8 | Open original |
Recovered source transcript
Download complete source text ↓97 lines · 3.0 KB
package recruitmatch
import (
"context"
"errors"
"time"
"github.com/jackc/pgx/v5"
"github.com/missionworkforce/missionmatch/internal/platform/audit"
"github.com/missionworkforce/missionmatch/internal/platform/tenant"
)
// ErrNoDatabase is returned when the service was built without a pool, which the handler maps to 503.
var ErrNoDatabase = errors.New("recruitmatch: no database configured")
// topJobsLimit bounds the ranked requirement list. It is a constant rather than a request parameter so
// the response size cannot be driven by a caller.
const topJobsLimit = 5
// Service computes the recruitMATCH dashboard. It reads only tables the other tools already write and
// owns no schema of its own.
type Service struct {
db *tenant.DB
audit *audit.Writer
clock func() time.Time
}
func NewService(db *tenant.DB) *Service { return &Service{db: db, clock: time.Now} }
// NewServiceWithAudit keeps parity with the other modules. The dashboard is read only today, so the
// writer is currently unused, but the constructor exists so wiring does not change when a write lands.
func NewServiceWithAudit(db *tenant.DB, aud *audit.Writer) *Service {
return &Service{db: db, audit: aud, clock: time.Now}
}
// RecruiterDashboard assembles the whole view inside ONE tenant transaction, so every panel observes the
// same snapshot and no two numbers on the screen can come from different states of the database.
//
// The transaction is also the RLS boundary: RunInTenantTx sets app.current_org_id transaction locally,
// buildTemps refuses to proceed if it is unset, and the temp tables are populated by RLS filtered reads,
// so they contain only this tenant's rows and are dropped at commit.
//
// No query in this module calls now() or current_date. Time enters only through asOf, so the same asOf
// over the same data produces a byte identical view.
func (s *Service) RecruiterDashboard(ctx context.Context, asOf time.Time) (View, error) {
if s.db == nil {
return View{}, ErrNoDatabase
}
orgID, _ := tenant.OrgFromContext(ctx)
asOfStr := asOf.Format("2006-01-02")
v := View{
AsOf: asOfStr,
OrganizationID: orgID,
GeneratedAt: asOf.UTC().Format(time.RFC3339),
EmptyPanels: notTracked(),
}
err := s.db.RunInTenantTx(ctx, func(ctx context.Context, tx pgx.Tx) error {
if err := buildTemps(ctx, tx, asOfStr); err != nil {
return err
}
h, err := headline(ctx, tx)
if err != nil {
return err
}
v.Headline = h
v.Funnel = buildFunnel(h.Identified, h.Screened, h.Submitted, h.Approved)
approved, rejected, pending, err := decisionCounts(ctx, tx)
if err != nil {
return err
}
v.Decisions = buildDecisions(approved, rejected, pending)
if v.BandMix, err = bandMix(ctx, tx); err != nil {
return err
}
if v.Clearance, err = clearanceRows(ctx, tx); err != nil {
return err
}
if v.TopJobs, err = topJobs(ctx, tx, topJobsLimit); err != nil {
return err
}
if v.Recruiters, err = recruiters(ctx, tx); err != nil {
return err
}
return nil
})
if err != nil {
return View{}, err
}
return v, nil
}