github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/syz-cluster/pkg/api/reporter.go (about)

     1  // Copyright 2025 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  package api
     5  
     6  import (
     7  	"context"
     8  	"net/url"
     9  	"strings"
    10  	"time"
    11  )
    12  
    13  type ReporterClient struct {
    14  	baseURL string
    15  }
    16  
    17  func NewReporterClient(url string) *ReporterClient {
    18  	return &ReporterClient{baseURL: strings.TrimRight(url, "/")}
    19  }
    20  
    21  type NextReportResp struct {
    22  	Report *SessionReport `json:"report"`
    23  }
    24  
    25  const LKMLReporter = "lkml"
    26  
    27  func (client ReporterClient) GetNextReport(ctx context.Context, reporter string) (*NextReportResp, error) {
    28  	v := url.Values{}
    29  	v.Add("reporter", reporter)
    30  	return postJSON[any, NextReportResp](ctx, client.baseURL+"/reports?"+v.Encode(), nil)
    31  }
    32  
    33  // ConfirmReport should be called to mark a report as sent.
    34  func (client ReporterClient) ConfirmReport(ctx context.Context, id string) error {
    35  	_, err := postJSON[any, any](ctx, client.baseURL+"/reports/"+id+"/confirm", nil)
    36  	return err
    37  }
    38  
    39  type UpstreamReportReq struct {
    40  	User string `json:"user"`
    41  }
    42  
    43  func (client ReporterClient) UpstreamReport(ctx context.Context, id string, req *UpstreamReportReq) error {
    44  	_, err := postJSON[UpstreamReportReq, any](ctx, client.baseURL+"/reports/"+id+"/upstream", req)
    45  	return err
    46  }
    47  
    48  func (client ReporterClient) InvalidateReport(ctx context.Context, id string) error {
    49  	_, err := postJSON[any, any](ctx, client.baseURL+"/reports/"+id+"/invalidate", nil)
    50  	return err
    51  }
    52  
    53  type RecordReplyReq struct {
    54  	MessageID string `json:"message_id"`
    55  	ReportID  string `json:"report_id"`
    56  	// If ReportID is not set, InReplyTo will help identify the original report.
    57  	InReplyTo string    `json:"in_reply_to"`
    58  	Reporter  string    `json:"reporter"`
    59  	Time      time.Time `json:"time"`
    60  }
    61  
    62  type RecordReplyResp struct {
    63  	New      bool   `json:"new"`
    64  	ReportID string `json:"report_id"` // or empty, if no original message was found
    65  }
    66  
    67  func (client ReporterClient) RecordReply(ctx context.Context, req *RecordReplyReq) (*RecordReplyResp, error) {
    68  	return postJSON[RecordReplyReq, RecordReplyResp](ctx, client.baseURL+"/reports/record_reply", req)
    69  }
    70  
    71  type LastReplyResp struct {
    72  	Time time.Time `json:"time"`
    73  }
    74  
    75  // Returns nil if no reply has ever been recorded.
    76  func (client ReporterClient) LastReply(ctx context.Context, reporter string) (*LastReplyResp, error) {
    77  	v := url.Values{}
    78  	v.Add("reporter", reporter)
    79  	return postJSON[any, LastReplyResp](ctx, client.baseURL+"/reports/last_reply?"+v.Encode(), nil)
    80  }