aboutsummaryrefslogtreecommitdiffstats
path: root/syz-cluster/pkg
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2025-06-28 17:30:50 +0200
committerAleksandr Nogikh <nogikh@google.com>2025-07-02 09:13:24 +0000
commit8eca022fb899f81facfd753a892d33ad794e2846 (patch)
tree38e3deed52689f467241e50c885c1c59582d75a0 /syz-cluster/pkg
parentd6d4e158298e946390e54d87a0a808a4238a24b4 (diff)
syz-cluster: make report reply tracking more flexible
Replace an UpdateReport() call with a RecordReply(). This will eventually allow us to support the email sender implementations for which we do not immediately know the MessageID of the reported message.
Diffstat (limited to 'syz-cluster/pkg')
-rw-r--r--syz-cluster/pkg/api/reporter.go14
-rw-r--r--syz-cluster/pkg/db/entities.go1
-rw-r--r--syz-cluster/pkg/db/migrations/1_initialize.down.sql1
-rw-r--r--syz-cluster/pkg/db/migrations/1_initialize.up.sql2
-rw-r--r--syz-cluster/pkg/db/report_repo.go11
-rw-r--r--syz-cluster/pkg/db/report_repo_test.go13
-rw-r--r--syz-cluster/pkg/db/spanner.go7
-rw-r--r--syz-cluster/pkg/reporter/api.go11
-rw-r--r--syz-cluster/pkg/reporter/api_test.go9
-rw-r--r--syz-cluster/pkg/service/discussion.go37
-rw-r--r--syz-cluster/pkg/service/report.go11
11 files changed, 31 insertions, 86 deletions
diff --git a/syz-cluster/pkg/api/reporter.go b/syz-cluster/pkg/api/reporter.go
index e73bdb668..ea83dfbe6 100644
--- a/syz-cluster/pkg/api/reporter.go
+++ b/syz-cluster/pkg/api/reporter.go
@@ -30,16 +30,6 @@ func (client ReporterClient) GetNextReport(ctx context.Context, reporter string)
return postJSON[any, NextReportResp](ctx, client.baseURL+"/reports?"+v.Encode(), nil)
}
-type UpdateReportReq struct {
- MessageID string `json:"message_id"`
-}
-
-// UpdateReport may be used to remember the message ID and the link to the discussion.
-func (client ReporterClient) UpdateReport(ctx context.Context, id string, req *UpdateReportReq) error {
- _, err := postJSON[UpdateReportReq, any](ctx, client.baseURL+"/reports/"+id+"/update", req)
- return err
-}
-
// ConfirmReport should be called to mark a report as sent.
func (client ReporterClient) ConfirmReport(ctx context.Context, id string) error {
_, err := postJSON[any, any](ctx, client.baseURL+"/reports/"+id+"/confirm", nil)
@@ -56,7 +46,9 @@ func (client ReporterClient) UpstreamReport(ctx context.Context, id string, req
}
type RecordReplyReq struct {
- MessageID string `json:"message_id"`
+ MessageID string `json:"message_id"`
+ ReportID string `json:"report_id"`
+ // If ReportID is not set, InReplyTo will help identify the original report.
InReplyTo string `json:"in_reply_to"`
Reporter string `json:"reporter"`
Time time.Time `json:"time"`
diff --git a/syz-cluster/pkg/db/entities.go b/syz-cluster/pkg/db/entities.go
index 13c440926..c914d7361 100644
--- a/syz-cluster/pkg/db/entities.go
+++ b/syz-cluster/pkg/db/entities.go
@@ -137,7 +137,6 @@ type SessionReport struct {
SessionID string `spanner:"SessionID"`
ReportedAt spanner.NullTime `spanner:"ReportedAt"`
Moderation bool `spanner:"Moderation"`
- MessageID string `spanner:"MessageID"`
Reporter string `spanner:"Reporter"`
}
diff --git a/syz-cluster/pkg/db/migrations/1_initialize.down.sql b/syz-cluster/pkg/db/migrations/1_initialize.down.sql
index 04bdb97e2..64c1fbb35 100644
--- a/syz-cluster/pkg/db/migrations/1_initialize.down.sql
+++ b/syz-cluster/pkg/db/migrations/1_initialize.down.sql
@@ -20,7 +20,6 @@ DROP INDEX SessionsByFinishedAt;
DROP INDEX NoDupFindings;
DROP INDEX NoDupSessionReports;
DROP INDEX SessionReportsByStatus;
-DROP INDEX SessionReportsByMessageID;
DROP TABLE ReportReplies;
DROP TABLE Findings;
diff --git a/syz-cluster/pkg/db/migrations/1_initialize.up.sql b/syz-cluster/pkg/db/migrations/1_initialize.up.sql
index baec817cb..139a9c21f 100644
--- a/syz-cluster/pkg/db/migrations/1_initialize.up.sql
+++ b/syz-cluster/pkg/db/migrations/1_initialize.up.sql
@@ -107,14 +107,12 @@ CREATE TABLE SessionReports (
SessionID STRING(36) NOT NULL, -- UUID
ReportedAt TIMESTAMP,
Moderation BOOL,
- MessageID STRING(512),
Reporter STRING(256),
CONSTRAINT FK_SessionReports FOREIGN KEY (SessionID) REFERENCES Sessions (ID),
) PRIMARY KEY(ID);
CREATE UNIQUE INDEX NoDupSessionReports ON SessionReports(SessionID, Moderation);
CREATE INDEX SessionReportsByStatus ON SessionReports (Reporter, ReportedAt);
-CREATE INDEX SessionReportsByMessageID ON SessionReports(Reporter, MessageID);
-- Replies on a session report.
CREATE TABLE ReportReplies (
diff --git a/syz-cluster/pkg/db/report_repo.go b/syz-cluster/pkg/db/report_repo.go
index 37a09a746..4e039c4a7 100644
--- a/syz-cluster/pkg/db/report_repo.go
+++ b/syz-cluster/pkg/db/report_repo.go
@@ -44,14 +44,3 @@ func (repo *ReportRepository) ListNotReported(ctx context.Context, reporter stri
addLimit(&stmt, limit)
return repo.readEntities(ctx, stmt)
}
-
-func (repo *ReportRepository) FindByMessageID(ctx context.Context, reporter, messageID string) (*SessionReport, error) {
- stmt := spanner.Statement{
- SQL: "SELECT * FROM `SessionReports` WHERE `Reporter` = @reporter AND `MessageID` = @messageID",
- Params: map[string]interface{}{
- "reporter": reporter,
- "messageID": messageID,
- },
- }
- return repo.readEntity(ctx, stmt)
-}
diff --git a/syz-cluster/pkg/db/report_repo_test.go b/syz-cluster/pkg/db/report_repo_test.go
index a0cee2a11..a9e23fd75 100644
--- a/syz-cluster/pkg/db/report_repo_test.go
+++ b/syz-cluster/pkg/db/report_repo_test.go
@@ -40,30 +40,17 @@ func TestReportRepository(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, list, 3)
- const messageID = "message-id"
err = reportRepo.Update(ctx, keys[0], func(rep *SessionReport) error {
rep.SetReportedAt(time.Now())
- rep.MessageID = messageID
return nil
})
assert.NoError(t, err)
-
t.Run("not-reported-count", func(t *testing.T) {
// Now one less.
list, err := reportRepo.ListNotReported(ctx, dummyReporter, 10)
assert.NoError(t, err)
assert.Len(t, list, 2)
})
- t.Run("find-by-id-found", func(t *testing.T) {
- report, err := reportRepo.FindByMessageID(ctx, dummyReporter, messageID)
- assert.NoError(t, err)
- assert.NotNil(t, report)
- })
- t.Run("find-by-id-empty", func(t *testing.T) {
- report, err := reportRepo.FindByMessageID(ctx, dummyReporter, "non-existing-id")
- assert.NoError(t, err)
- assert.Nil(t, report)
- })
}
func TestSessionsWithoutReports(t *testing.T) {
diff --git a/syz-cluster/pkg/db/spanner.go b/syz-cluster/pkg/db/spanner.go
index 9bdeee53b..1aee715f8 100644
--- a/syz-cluster/pkg/db/spanner.go
+++ b/syz-cluster/pkg/db/spanner.go
@@ -343,10 +343,3 @@ func (g *genericEntityOps[EntityType, KeyType]) readEntities(ctx context.Context
defer iter.Stop()
return readEntities[EntityType](iter)
}
-
-func (g *genericEntityOps[EntityType, KeyType]) readEntity(ctx context.Context,
- stmt spanner.Statement) (*EntityType, error) {
- iter := g.client.Single().Query(ctx, stmt)
- defer iter.Stop()
- return readOne[EntityType](iter)
-}
diff --git a/syz-cluster/pkg/reporter/api.go b/syz-cluster/pkg/reporter/api.go
index e06e1312c..89bd21eb1 100644
--- a/syz-cluster/pkg/reporter/api.go
+++ b/syz-cluster/pkg/reporter/api.go
@@ -29,7 +29,6 @@ func NewAPIServer(env *app.AppEnvironment) *APIServer {
func (s *APIServer) Mux() *http.ServeMux {
mux := http.NewServeMux()
- mux.HandleFunc("/reports/{report_id}/update", s.updateReport)
mux.HandleFunc("/reports/{report_id}/upstream", s.upstreamReport)
mux.HandleFunc("/reports/{report_id}/confirm", s.confirmReport)
mux.HandleFunc("/reports/record_reply", s.recordReply)
@@ -39,16 +38,6 @@ func (s *APIServer) Mux() *http.ServeMux {
}
// nolint: dupl
-func (s *APIServer) updateReport(w http.ResponseWriter, r *http.Request) {
- req := api.ParseJSON[api.UpdateReportReq](w, r)
- if req == nil {
- return // TODO: return StatusBadRequest here and below.
- }
- err := s.reportService.Update(r.Context(), r.PathValue("report_id"), req)
- reply[interface{}](w, nil, err)
-}
-
-// nolint: dupl
func (s *APIServer) upstreamReport(w http.ResponseWriter, r *http.Request) {
req := api.ParseJSON[api.UpstreamReportReq](w, r)
if req == nil {
diff --git a/syz-cluster/pkg/reporter/api_test.go b/syz-cluster/pkg/reporter/api_test.go
index 9f94ce5b6..14c61f5bc 100644
--- a/syz-cluster/pkg/reporter/api_test.go
+++ b/syz-cluster/pkg/reporter/api_test.go
@@ -108,15 +108,18 @@ func TestReplyReporting(t *testing.T) {
err = reportClient.ConfirmReport(ctx, reportID)
assert.NoError(t, err)
- err = reportClient.UpdateReport(ctx, reportID, &api.UpdateReportReq{
- MessageID: "message-id",
+ const reportMessageID = "message-id"
+ _, err = reportClient.RecordReply(ctx, &api.RecordReplyReq{
+ MessageID: reportMessageID,
+ ReportID: reportID,
+ Reporter: api.LKMLReporter,
})
assert.NoError(t, err)
// Direct reply to the report.
resp, err := reportClient.RecordReply(ctx, &api.RecordReplyReq{
MessageID: "direct-reply-id",
- InReplyTo: "message-id",
+ InReplyTo: reportMessageID,
Reporter: api.LKMLReporter,
Time: time.Now(),
})
diff --git a/syz-cluster/pkg/service/discussion.go b/syz-cluster/pkg/service/discussion.go
index f4be21fbd..8c17f1623 100644
--- a/syz-cluster/pkg/service/discussion.go
+++ b/syz-cluster/pkg/service/discussion.go
@@ -31,22 +31,10 @@ func NewDiscussionService(env *app.AppEnvironment) *DiscussionService {
}
func (d *DiscussionService) RecordReply(ctx context.Context, req *api.RecordReplyReq) (*api.RecordReplyResp, error) {
- // First check if the message was a directl reply to the report.
- report, err := d.reportRepo.FindByMessageID(ctx, req.Reporter, req.InReplyTo)
+ reportID, err := d.identifyReport(ctx, req)
if err != nil {
- return nil, fmt.Errorf("failed to search among the reports: %w", err)
- }
- var reportID string
- if report != nil {
- reportID = report.ID
- } else {
- // Now try to find a matching reply.
- reportID, err = d.reportReplyRepo.FindParentReportID(ctx, req.Reporter, req.InReplyTo)
- if err != nil {
- return nil, fmt.Errorf("failed to search among the replies: %w", err)
- }
- }
- if reportID == "" {
+ return nil, err
+ } else if reportID == "" {
// We could not find the related report.
return &api.RecordReplyResp{}, nil
}
@@ -78,3 +66,22 @@ func (d *DiscussionService) LastReply(ctx context.Context, reporter string) (*ap
}
return &api.LastReplyResp{}, nil
}
+
+func (d *DiscussionService) identifyReport(ctx context.Context, req *api.RecordReplyReq) (string, error) {
+ // If the report ID was passed explicitly, just verify it.
+ if req.ReportID != "" {
+ report, err := d.reportRepo.GetByID(ctx, req.ReportID)
+ if err != nil {
+ return "", fmt.Errorf("failed to query the report: %w", err)
+ } else if report != nil {
+ return report.ID, nil
+ }
+ return "", nil
+ }
+ // Now try to find a matching reply.
+ reportID, err := d.reportReplyRepo.FindParentReportID(ctx, req.Reporter, req.InReplyTo)
+ if err != nil {
+ return "", fmt.Errorf("search among the replies failed: %w", err)
+ }
+ return reportID, nil
+}
diff --git a/syz-cluster/pkg/service/report.go b/syz-cluster/pkg/service/report.go
index 5e6b6756a..4faef5b1e 100644
--- a/syz-cluster/pkg/service/report.go
+++ b/syz-cluster/pkg/service/report.go
@@ -30,17 +30,6 @@ func NewReportService(env *app.AppEnvironment) *ReportService {
var ErrReportNotFound = errors.New("report is not found")
-func (rs *ReportService) Update(ctx context.Context, id string, req *api.UpdateReportReq) error {
- err := rs.reportRepo.Update(ctx, id, func(rep *db.SessionReport) error {
- rep.MessageID = req.MessageID
- return nil
- })
- if errors.Is(err, db.ErrEntityNotFound) {
- return ErrReportNotFound
- }
- return err
-}
-
func (rs *ReportService) Confirm(ctx context.Context, id string) error {
err := rs.reportRepo.Update(ctx, id, func(rep *db.SessionReport) error {
if rep.ReportedAt.IsNull() {