package feedback import ( "context" "errors" "log/slog" "git.golem15.com/golem15/sm-feedback-plugin/classes" "git.golem15.com/golem15/summercms/modules/conga" "git.golem15.com/golem15/summercms/modules/pact" "gocloud.dev/blob" "gorm.io/gorm" ) var _ pact.HasJobs = (*Plugin)(nil) // Jobs registers the G15Office sync worker on the feedback queue, with // SyncFeedbackToG15Office::$tries attempts (River's backoff replaces PHP's // fixed 30 s between them). func (p *Plugin) Jobs() []pact.Job { return []pact.Job{ conga.Job(p.syncG15Office, conga.OnQueue(classes.SyncG15OfficeQueue), conga.MaxAttempts(3)), } } // syncG15Office resolves the app services per run (Boot runs before serve // publishes the database), files the submission and completes its // summer_jobs row. The submission's content, the token and the URLs are // never logged; only the submission id is. func (p *Plugin) syncG15Office(ctx context.Context, args classes.SyncG15OfficeArgs) error { if p.app == nil { return errors.New("feedback: app is not booted") } gdb, ok := p.app.Lookup[*gorm.DB]() if !ok || gdb == nil { return errors.New("feedback: database is not published") } bucket, _ := p.app.Lookup[*blob.Bucket]() err := classes.SyncG15Office(ctx, classes.SyncDeps{ DB: gdb, Bucket: bucket, Client: classes.NewG15OfficeClient(p.app.Config), }, args) if err != nil { slog.Default().Warn("golem15.feedback: filing a submission in G15Office failed", slog.Uint64("submission_id", uint64(args.SubmissionID))) return err } // The task is filed: a failed completion is logged, not returned, so // River does not retry a job whose side effect already happened. if id, ok := conga.JobID(ctx); ok { if m, err := conga.From(p.app); err == nil { if err := m.CompleteJob(ctx, id, nil); err != nil { slog.Default().Warn("golem15.feedback: completing the G15Office job failed", slog.Uint64("submission_id", uint64(args.SubmissionID)), slog.Uint64("job_id", uint64(id))) } } } return nil }