Files
sm-feedback-plugin/jobs.go
Jakub Zych e52b1ecd2c fix(14): WR-06 never file a second G15Office task on a job retry
A retry after a failed attachment or a failed CompleteJob created another
task for the same report. The task id is now stored as soon as the task
is created, a retry with a stored id only re-attempts the attachment, a
sent submission with a task id is skipped, and a CompleteJob failure is
logged instead of returned.
2026-10-04 08:10:30 +02:00

59 lines
2.0 KiB
Go

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
}