diff --git a/internal/csat/csat.go b/internal/csat/csat.go index 34a8e6bf..0d0c01a9 100644 --- a/internal/csat/csat.go +++ b/internal/csat/csat.go @@ -106,11 +106,16 @@ func (m *Manager) UpdateResponse(uuid string, score int, feedback string, meta j meta = json.RawMessage(`{}`) } - _, err = m.q.Update.Exec(uuid, score, feedback, meta) + res, err := m.q.Update.Exec(uuid, score, feedback, meta) if err != nil { m.lo.Error("error updating CSAT", "error", err) return envelope.NewError(envelope.GeneralError, m.i18n.T("globals.messages.somethingWentWrong"), nil) } + // The UPDATE is guarded by response_timestamp IS NULL, so a concurrent + // request that already recorded a response updates zero rows here. + if n, _ := res.RowsAffected(); n == 0 { + return envelope.NewError(envelope.InputError, m.i18n.T("csat.alreadySubmitted"), nil) + } return nil } diff --git a/internal/csat/queries.sql b/internal/csat/queries.sql index faa2a6cd..b8e68d65 100644 --- a/internal/csat/queries.sql +++ b/internal/csat/queries.sql @@ -23,4 +23,4 @@ SET rating = $2, feedback = $3, meta = COALESCE($4::jsonb, '{}'), response_timestamp = NOW() -WHERE uuid = $1; +WHERE uuid = $1 AND response_timestamp IS NULL;