ThatAIguyA world of ideas

THE CONSCIOUSNESS ATLAS
Read. Experiment. Follow the evidence.

ConsciousnessLearn · Explore · Joseph Anady

Janady13/missionmatch / internal/modules/careers/scheduling.go

Source identity, revision, and recovered contents.

GitHub sourceSource text recovered

Source identity

FieldRecorded value
Source typeGitHub source
Original recordJanady13/missionmatch / internal/modules/careers/scheduling.go
DateNot specified in source
RepositoryJanady13/missionmatch
Revision720d47fae476b0d38477ae4401697d372b1ea2bc
Branchfeature/opportunity-to-outcome
Path or archive memberinternal/modules/careers/scheduling.go
Original SHA-256f2e6399f411d4af359cb1d858d85db050a3cef2baa7dbb68cc58f160fef34891
Git blob SHA3956fa2263bd6b9474f3dde9a7536144934d0ca8
Reading-copy SHA-256f2e6399f411d4af359cb1d858d85db050a3cef2baa7dbb68cc58f160fef34891

Original source ↗

Recovered source transcript

package careers // INTERVIEW SCHEDULING. A recruiter offers times, the candidate picks one, and everybody gets a calendar // invite. // // It lives in this package rather than in recruiting because it shares the thing that is hard: a PUBLIC // surface reached by somebody with no account, resolved through a SECURITY DEFINER function because a request // with no session has no organization and row level security correctly denies it everything. Putting it // beside the careers page keeps one copy of that pattern instead of two. // // AN OFFER IS NOT AN INTERVIEW. Offering three times creates an offer with three slots; the interviews row // appears only when somebody picks one. Creating the interview up front and moving it would leave a pipeline // full of interviews that never happened, which is exactly the number a hiring manager asks about. import ( "context" "encoding/json" "errors" "fmt" "strings" "time" "github.com/google/uuid" "github.com/jackc/pgx/v5" "github.com/missionworkforce/missionmatch/internal/platform/audit" "github.com/missionworkforce/missionmatch/internal/platform/icalendar" "github.com/missionworkforce/missionmatch/internal/platform/mailer" "github.com/missionworkforce/missionmatch/internal/platform/orgsettings" "github.com/missionworkforce/missionmatch/internal/platform/tenant" ) var ( ErrOfferUnavailable = errors.New("careers: that scheduling link is no longer available") ErrSlotTaken = errors.New("careers: that time is no longer available") ErrNoSlots = errors.New("careers: an offer needs at least one time") ) // OfferInput creates a scheduling offer. type OfferInput struct { ApplicationID string `json:"application_id"` Kind string `json:"kind"` Round int `json:"round"` DurationMins int `json:"duration_mins"` Location string `json:"location_text"` Interviewer string `json:"interviewer"` Message string `json:"message"` ExpiresInDays int `json:"expires_in_days"` Slots []time.Time `json:"slots"` } // PublicOffer is what the candidate sees. It carries no pipeline detail, no other candidate, and no internal // identifier: a person choosing a time needs the role, the employer, the length, and the times. type PublicOffer struct { Token string `json:"token"` Employer string `json:"employer"` Role string `json:"role"` Candidate string `json:"candidate_name"` Kind string `json:"kind"` Duration int `json:"duration_mins"` Location string `json:"location_text,omitempty"` Interviewer string `json:"interviewer,omitempty"` Message string `json:"message,omitempty"` ExpiresAt time.Time `json:"expires_at"` Slots []PublicSlot `json:"slots"` } type PublicSlot struct { ID string `json:"id"` StartsAt time.Time `json:"starts_at"` EndsAt time.Time `json:"ends_at"` } var validInterviewKind = map[string]bool{ "phone_screen": true, "technical": true, "client": true, "panel": true, "final": true, } // CreateOffer records an offer and its times, and returns the public link. func (s *Service) CreateOffer(ctx context.Context, in OfferInput, ip, ua string) (string, string, error) { if s.db == nil { return "", "", ErrNoDatabase } appID, err := uuid.Parse(strings.TrimSpace(in.ApplicationID)) if err != nil { return "", "", errors.New("careers: an application identifier is required") } if in.Kind == "" { in.Kind = "client" } if !validInterviewKind[in.Kind] { return "", "", errors.New("careers: unknown interview kind") } if in.Round <= 0 { in.Round = 1 } if in.DurationMins <= 0 { in.DurationMins = 45 } if in.DurationMins < 5 || in.DurationMins > 480 { return "", "", errors.New("careers: an interview runs between five minutes and eight hours") } if in.ExpiresInDays <= 0 { in.ExpiresInDays = 7 } if in.ExpiresInDays > 60 { return "", "", errors.New("careers: a scheduling link may live at most sixty days") } if len(in.Slots) == 0 { return "", "", ErrNoSlots } if len(in.Slots) > 20 { return "", "", errors.New("careers: offer at most twenty times, a longer list is harder to choose from") } // A time in the past is refused rather than silently offered, because a candidate clicking it would get // a conflict they cannot explain. now := time.Now().UTC() for _, t := range in.Slots { if t.Before(now) { return "", "", errors.New("careers: one of those times is in the past") } } token, err := NewPostingToken() if err != nil { return "", "", err } actor := actorPtr(ctx) // A uuid, NOT the text form. Scanning id::text and then passing that string back as a uuid parameter is // a conversion pgx should not have to guess at, and it is what made the first version of this fail with // a 500 that the handler turned into a generic message. var offerUUID uuid.UUID err = s.db.RunInTenantTx(ctx, func(ctx context.Context, tx pgx.Tx) error { e := tx.QueryRow(ctx, ` INSERT INTO interview_offers (organization_id, application_id, public_token, kind, round, duration_mins, location_text, interviewer, message, expires_at, created_by) VALUES (NULLIF(current_setting('app.current_org_id', true), '')::uuid, $1,$2,$3,$4,$5,$6,$7,$8, now() + make_interval(days => $9), $10) RETURNING id`, appID, token, in.Kind, in.Round, in.DurationMins, clip(in.Location, 500), clip(in.Interviewer, 200), clip(in.Message, 2000), in.ExpiresInDays, actor).Scan(&offerUUID) if e != nil { return e } for _, t := range in.Slots { // The end is computed HERE rather than in SQL. Writing it as "$2 + make_interval(...)" made // Postgres deduce two different types for the same parameter, once as a timestamp column and // once as the left side of an addition, and it refused the statement with SQLSTATE 42P08. Doing // the arithmetic in Go is also simply clearer about what a slot's length is. start := t.UTC() end := start.Add(time.Duration(in.DurationMins) * time.Minute) if _, e := tx.Exec(ctx, ` INSERT INTO interview_offer_slots (organization_id, offer_id, starts_at, ends_at) VALUES (NULLIF(current_setting('app.current_org_id', true), '')::uuid, $1, $2, $3)`, offerUUID, start, end); e != nil { return e } } if s.aud == nil { return nil } body, _ := json.Marshal(map[string]any{ "application_id": appID.String(), "kind": in.Kind, "slots": len(in.Slots), }) return s.aud.OnTx(ctx, tx, audit.Event{ Action: "interview.offer.created", ActorUserID: actor, EntityType: "interview_offer", EntityID: offerUUID.String(), NewValues: body, IPAddress: ip, UserAgent: ua, }) }) return offerUUID.String(), token, err } // GetPublicOffer serves the candidate's choosing page. func (s *Service) GetPublicOffer(ctx context.Context, slug, token string) (PublicOffer, error) { var o PublicOffer if s.db == nil { return o, ErrNoDatabase } if !validToken(token) { return o, ErrOfferUnavailable } org, err := s.resolveOrg(ctx, slug) if err != nil { return o, ErrOfferUnavailable } err = s.db.RunInTenantTx(tenant.WithOrg(ctx, org.String()), func(ctx context.Context, tx pgx.Tx) error { var offerID uuid.UUID e := tx.QueryRow(ctx, `SELECT offer_id, kind, duration_mins, location_text, interviewer, message, expires_at, candidate_name, requisition_title, organization_name FROM public_interview_offer($1)`, token). Scan(&offerID, &o.Kind, &o.Duration, &o.Location, &o.Interviewer, &o.Message, &o.ExpiresAt, &o.Candidate, &o.Role, &o.Employer) if errors.Is(e, pgx.ErrNoRows) { return ErrOfferUnavailable } if e != nil { return e } rows, e := tx.Query(ctx, `SELECT id::text, starts_at, ends_at FROM interview_offer_slots WHERE offer_id = $1 AND available AND starts_at > now() ORDER BY starts_at`, offerID) if e != nil { return e } defer rows.Close() o.Slots = []PublicSlot{} for rows.Next() { var sl PublicSlot if e := rows.Scan(&sl.ID, &sl.StartsAt, &sl.EndsAt); e != nil { return e } o.Slots = append(o.Slots, sl) } return rows.Err() }) o.Token = token return o, err } // BookSlot is the candidate choosing a time. It creates the interview, closes the offer, and sends the // calendar invite. // // THE SLOT IS TAKEN UNDER A CONDITIONAL UPDATE, not a read then write. Two candidates cannot race here, but a // candidate double clicking absolutely can, and a check followed by an update would book the same time twice // and create two interviews. func (s *Service) BookSlot(ctx context.Context, slug, token, slotID, ip string) (time.Time, error) { var when time.Time if s.db == nil { return when, ErrNoDatabase } if !validToken(token) { return when, ErrOfferUnavailable } sid, err := uuid.Parse(strings.TrimSpace(slotID)) if err != nil { return when, ErrSlotTaken } org, err := s.resolveOrg(ctx, slug) if err != nil { return when, ErrOfferUnavailable } tctx := tenant.WithOrg(ctx, org.String()) var invite mailer.Message var cfg mailer.Config var send bool err = s.db.RunInTenantTx(tctx, func(ctx context.Context, tx pgx.Tx) error { var offerID uuid.UUID var kind, location, interviewer, candName, role, employer string var duration, round int var appID uuid.UUID e := tx.QueryRow(ctx, ` SELECT o.id, o.application_id, o.kind, o.round, o.duration_mins, o.location_text, o.interviewer, c.full_name, r.title, org.name FROM interview_offers o JOIN applications a ON a.id = o.application_id JOIN candidates c ON c.id = a.candidate_id JOIN requisitions r ON r.id = a.requisition_id JOIN organizations org ON org.id = o.organization_id WHERE o.public_token = $1 AND o.status = 'open' AND o.expires_at > now()`, token). Scan(&offerID, &appID, &kind, &round, &duration, &location, &interviewer, &candName, &role, &employer) if errors.Is(e, pgx.ErrNoRows) { return ErrOfferUnavailable } if e != nil { return e } // CONDITIONAL: the row is only taken if it is still available. A double click loses the race here // rather than producing a second interview. var ends time.Time e = tx.QueryRow(ctx, `UPDATE interview_offer_slots SET available = false WHERE id = $1 AND offer_id = $2 AND available AND starts_at > now() RETURNING starts_at, ends_at`, sid, offerID).Scan(&when, &ends) if errors.Is(e, pgx.ErrNoRows) { return ErrSlotTaken } if e != nil { return e } var interviewID uuid.UUID e = tx.QueryRow(ctx, ` INSERT INTO interviews (organization_id, application_id, round, kind, scheduled_at, outcome, interviewer) VALUES (NULLIF(current_setting('app.current_org_id', true), '')::uuid, $1,$2,$3,$4,'scheduled',$5) RETURNING id`, appID, round, kind, when, interviewer).Scan(&interviewID) if e != nil { return e } if _, e := tx.Exec(ctx, `UPDATE interview_offers SET status = 'booked', booked_slot_id = $2, interview_id = $3, updated_at = now() WHERE id = $1`, offerID, sid, interviewID); e != nil { return e } // The remaining times are withdrawn, because an offer that has been taken should not still show // choices a candidate could click. if _, e := tx.Exec(ctx, `UPDATE interview_offer_slots SET available = false WHERE offer_id = $1 AND id <> $2`, offerID, sid); e != nil { return e } // The invitation is ASSEMBLED here and SENT after the transaction commits, so a mail failure cannot // roll back a booking the candidate has already been told about. var candEmail string _ = tx.QueryRow(ctx, `SELECT coalesce(c.email,'') FROM applications a JOIN candidates c ON c.id = a.candidate_id WHERE a.id = $1`, appID).Scan(&candEmail) if candEmail != "" && s.settings != nil { st, se := s.settings.LoadTx(ctx, tx) if se == nil && st.EmailDeliveryEnabled && st.SMTPVerifiedAt != nil && st.SMTPHost != "" && st.EmailFromAddress != "" { pw, _ := s.settings.OpenSecret(ctx, tx, orgsettings.SecretSMTPPassword) cfg = mailer.Config{ Host: st.SMTPHost, Port: st.SMTPPort, Username: st.SMTPUsername, Password: pw, Security: st.SMTPSecurity, From: st.EmailFromAddress, FromName: st.EmailFromName, ReplyTo: st.EmailReplyTo, } summary := fmt.Sprintf("Interview, %s, with %s", role, employer) ics := icalendar.Render(icalendar.Event{ UID: "interview-" + interviewID.String() + "@missionmatch", Summary: summary, Description: strings.TrimSpace("Interview for " + role + ". " + interviewer), Location: location, Start: when, End: ends, OrganizerName: st.EmailFromName, OrganizerEmail: st.EmailFromAddress, AttendeeName: candName, AttendeeEmail: candEmail, }) invite = mailer.Message{ To: candEmail, Subject: summary, Body: fmt.Sprintf("Your interview is confirmed for %s.\r\n\r\n%s\r\n\r\n%s\r\n", when.UTC().Format("Monday 2 January 2006 at 15:04 MST"), location, "A calendar invitation is attached."), Calendar: ics, CalendarMethod: "REQUEST", } send = true } } if s.aud == nil { return nil } body, _ := json.Marshal(map[string]any{ "offer_id": offerID.String(), "interview_id": interviewID.String(), "scheduled_at": when, }) return s.aud.OnTx(ctx, tx, audit.Event{ Action: "interview.booked", EntityType: "interview", EntityID: interviewID.String(), NewValues: body, IPAddress: ip, }) }) if err != nil { return when, err } if send { sctx, cancel := context.WithTimeout(ctx, 30*time.Second) defer cancel() // A failed invitation does NOT fail the booking. The time is taken and the interview exists; the // candidate simply did not get a calendar entry, which a recruiter can fix by hand. _ = mailer.Send(sctx, cfg, invite) } return when, nil } // WithdrawOffer closes an offer nobody booked. func (s *Service) WithdrawOffer(ctx context.Context, id uuid.UUID, ip, ua string) error { if s.db == nil { return ErrNoDatabase } actor := actorPtr(ctx) return s.db.RunInTenantTx(ctx, func(ctx context.Context, tx pgx.Tx) error { tag, e := tx.Exec(ctx, `UPDATE interview_offers SET status = 'withdrawn', updated_at = now() WHERE id = $1 AND status = 'open'`, id) if e != nil { return e } if tag.RowsAffected() == 0 { return ErrNotFound } if _, e := tx.Exec(ctx, `UPDATE interview_offer_slots SET available = false WHERE offer_id = $1`, id); e != nil { return e } if s.aud == nil { return nil } return s.aud.OnTx(ctx, tx, audit.Event{ Action: "interview.offer.withdrawn", ActorUserID: actor, EntityType: "interview_offer", EntityID: id.String(), IPAddress: ip, UserAgent: ua, }) }) } // ListOffers shows a recruiter what is outstanding. func (s *Service) ListOffers(ctx context.Context) ([]map[string]any, error) { if s.db == nil { return nil, ErrNoDatabase } out := make([]map[string]any, 0, 32) err := s.db.RunInTenantTx(ctx, func(ctx context.Context, tx pgx.Tx) error { rows, e := tx.Query(ctx, ` SELECT o.id::text, o.public_token, o.status, o.kind, o.expires_at, c.full_name, r.title, (SELECT count(*)::int FROM interview_offer_slots s WHERE s.offer_id = o.id AND s.available) FROM interview_offers o JOIN applications a ON a.id = o.application_id JOIN candidates c ON c.id = a.candidate_id JOIN requisitions r ON r.id = a.requisition_id ORDER BY o.created_at DESC LIMIT 200`) if e != nil { return e } defer rows.Close() for rows.Next() { var id, token, status, kind, cand, role string var exp time.Time var open int if e := rows.Scan(&id, &token, &status, &kind, &exp, &cand, &role, &open); e != nil { return e } out = append(out, map[string]any{ "id": id, "public_token": token, "status": status, "kind": kind, "expires_at": exp, "candidate_name": cand, "role": role, "times_available": open, }) } return rows.Err() }) return out, err }

Search equations, builds, manuscript text, and recovered sources.