package fetcher import ( "context" "errors" "log/slog" "time" "git.avsdev.uk/Go/job-search/internal/config" "git.avsdev.uk/Go/job-search/internal/findatender" "git.avsdev.uk/Go/job-search/internal/types" "git.avsdev.uk/Go/job-search/internal/util" ) // ftsMockSource is the Source of the opportunities the mock source fetches. const ftsMockSource = "FTSMock" // runFTSMock polls the mock Find a Tender server every configured interval // until ctx is cancelled, passing what each run fetches to save. func runFTSMock( ctx context.Context, logger *slog.Logger, conf *config.JSCSourceFTSMock, save saveFunc, since time.Time, ) error { if conf.APIRoot == "" { return errors.New("fetcher: sources.fts_mock.api_root is not set") } worker := util.Worker{ Logger: tagged(logger), Name: ftsMockSource, Interval: conf.Interval, Task: func(ctx context.Context, since time.Time) error { opportunities, err := fetchFTSMock(ctx, logger, conf, since) if err != nil { return err } return save(ctx, logger, opportunities) }, } return worker.Run(ctx, since) } // fetchFTSMock fetches everything updated since since from the mock Find a // Tender server. The mock serves the real API's format, so the converter // marks the opportunities as Find a Tender's; their Source is set to // ftsMockSource here instead. Finding nothing returns ErrNoNewReleases, so // the worker still moves its checkpoint on. func fetchFTSMock( ctx context.Context, logger *slog.Logger, conf *config.JSCSourceFTSMock, since time.Time, ) ([]*types.Opportunity, error) { opportunities, err := util.AwaitTask( ctx, func() ([]*types.Opportunity, error) { findATender := findatender.New( conf.APIRoot, findatender.WithLogger(logger), ) defer findATender.Shutdown() return findATender.With(ctx).FetchAll(since, true) }, ) if errors.Is(err, findatender.ErrNoReleases) { return nil, ErrNoNewReleases } if err != nil { return nil, err } for _, opportunity := range opportunities { opportunity.Source = ftsMockSource } tagged(logger).Info("fetched opportunities", "count", len(opportunities)) return opportunities, nil }