internal/bigquery: add vulndb requests
Read and write data about vulndb requests to BigQuery.
Change-Id: I87c1e4ec506ee1fd55c9c46aff235c961bd725a5
Reviewed-on: https://go-review.googlesource.com/c/pkgsite-metrics/+/465216
Auto-Submit: Julie Qiu <julieqiu@google.com>
Run-TryBot: Julie Qiu <julieqiu@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Julie Qiu <julieqiu@google.com>
diff --git a/internal/bigquery/bigquery_test.go b/internal/bigquery/bigquery_test.go
index 34d85fb..0715f43 100644
--- a/internal/bigquery/bigquery_test.go
+++ b/internal/bigquery/bigquery_test.go
@@ -16,6 +16,7 @@
"cloud.google.com/go/civil"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
+ "golang.org/x/exp/slices"
"golang.org/x/pkgsite-metrics/internal/version"
"google.golang.org/api/iterator"
)
@@ -196,6 +197,39 @@
}
})
+ t.Run("request counts", func(t *testing.T) {
+ date := func(y, m, d int) civil.Date {
+ return civil.Date{Year: y, Month: time.Month(m), Day: d}
+ }
+
+ must(client.CreateTable(ctx, VulnDBRequestTableName))
+ defer client.Table(VulnDBRequestTableName).Delete(ctx)
+ counts := []*VulnDBRequestCount{
+ {Date: date(2022, 10, 1), Count: 1},
+ {Date: date(2022, 10, 3), Count: 3},
+ {Date: date(2022, 10, 4), Count: 4},
+ }
+ for _, row := range counts {
+ must(client.Upload(ctx, VulnDBRequestTableName, row))
+ }
+ // Insert duplicates with a later time; we expect to get these, not the originals.
+ time.Sleep(50 * time.Millisecond)
+ for _, row := range counts {
+ row.Count++
+ must(client.Upload(ctx, VulnDBRequestTableName, row))
+ }
+
+ got, err := readVulnDBRequestCounts(ctx, client)
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := slices.Clone(counts)
+ slices.SortFunc(want, func(c1, c2 *VulnDBRequestCount) bool { return c1.Date.After(c2.Date) })
+ if diff := cmp.Diff(want, got, cmpopts.IgnoreFields(VulnDBRequestCount{}, "InsertedAt")); diff != "" {
+ t.Errorf("mismatch (-want, +got):\n%s", diff)
+ }
+ })
+
}
func readTable[T any](ctx context.Context, table *bq.Table, newT func() *T) ([]*T, error) {
diff --git a/internal/bigquery/vulndb_requests.go b/internal/bigquery/vulndb_requests.go
new file mode 100644
index 0000000..b9bc955
--- /dev/null
+++ b/internal/bigquery/vulndb_requests.go
@@ -0,0 +1,78 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Track daily counts of requests made to vuln.go.dev in BigQuery.
+
+package bigquery
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ bq "cloud.google.com/go/bigquery"
+ "cloud.google.com/go/civil"
+ "golang.org/x/pkgsite-metrics/internal/derrors"
+)
+
+const (
+ // Vuln DB requests live in their own dataset that doesn't vary.
+ VulnDBRequestDatasetName = "vulndb"
+ VulnDBRequestTableName = "requests"
+)
+
+type VulnDBRequestCount struct {
+ Date civil.Date `bigquery:"date"`
+ Count int `bigquery:"count"`
+ InsertedAt time.Time `bigquery:"inserted_at"`
+}
+
+func init() {
+ s, err := bq.InferSchema(VulnDBRequestCount{})
+ if err != nil {
+ panic(err)
+ }
+ addTable(VulnDBRequestTableName, s)
+}
+
+// SetUploadTime is used by Client.Upload.
+func (v *VulnDBRequestCount) SetUploadTime(t time.Time) { v.InsertedAt = t }
+
+func WriteVulnDBRequestCounts(ctx context.Context, projectID string, rcs []*VulnDBRequestCount) (err error) {
+ defer derrors.Wrap(&err, "WriteVulnDBRequestCounts(%s)", projectID)
+ c, err := NewClientCreate(ctx, projectID, VulnDBRequestDatasetName)
+ if err != nil {
+ return err
+ }
+ if err := c.CreateTable(ctx, VulnDBRequestTableName); err != nil {
+ return err
+ }
+ return UploadMany(ctx, c, VulnDBRequestTableName, rcs, 0)
+}
+
+// ReadVulnDBRequestCounts returns daily counts for requests to the vuln DB, with the most recent first.
+func ReadVulnDBRequestCounts(ctx context.Context, projectID string) (_ []*VulnDBRequestCount, err error) {
+ defer derrors.Wrap(&err, "ReadVulnDBRequestCounts(%s)", projectID)
+
+ c, err := NewClient(ctx, projectID, VulnDBRequestDatasetName)
+ if err != nil {
+ return nil, err
+ }
+ return readVulnDBRequestCounts(ctx, c)
+}
+
+func readVulnDBRequestCounts(ctx context.Context, c *Client) (_ []*VulnDBRequestCount, err error) {
+ // Select the most recently inserted row for each date.
+ q := fmt.Sprintf("(%s) ORDER BY date DESC",
+ partitionQuery(c.FullTableName(VulnDBRequestTableName), "date", "inserted_at DESC"))
+ iter, err := c.Query(ctx, q)
+ if err != nil {
+ return nil, err
+ }
+ counts, err := All[VulnDBRequestCount](iter)
+ if err != nil {
+ return nil, err
+ }
+ return counts, nil
+}
diff --git a/static/worker.tmpl b/static/index.tmpl
similarity index 100%
rename from static/worker.tmpl
rename to static/index.tmpl