Skip to content

Commit

Permalink
validate stuff (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
icco authored Sep 15, 2024
1 parent 3e8c7a0 commit 89a50a5
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 7 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/icco/gutil v0.0.0-20231226055340-e0dfddaad70c
github.com/namsral/flag v1.7.4-pre
go.uber.org/zap v1.27.0
google.golang.org/api v0.197.0
)

require (
Expand Down Expand Up @@ -53,7 +54,6 @@ require (
golang.org/x/time v0.6.0 // indirect
golang.org/x/tools v0.25.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
google.golang.org/api v0.197.0 // indirect
google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
Expand Down
47 changes: 46 additions & 1 deletion lib/analytics.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"cloud.google.com/go/bigquery"
"cloud.google.com/go/civil"
"google.golang.org/api/iterator"
)

// WebVital is a a version of https://web.dev/vitals/.
Expand Down Expand Up @@ -40,6 +41,18 @@ type WebVital struct {
Service bigquery.NullString
}

func (wv *WebVital) Validate() error {
if !wv.Service.Valid {
return fmt.Errorf("service is null")
}

if wv.Service.StringVal == "" {
return fmt.Errorf("service is empty")
}

return nil
}

// ParseAnalytics parses a webvitals request body.
func ParseAnalytics(body, service string) (*WebVital, error) {
now := civil.DateTimeOf(time.Now())
Expand Down Expand Up @@ -85,6 +98,12 @@ func getAnalyticsSchema() (bigquery.Schema, error) {

// WriteAnalyticsToBigQuery saves a webvital to bq.
func WriteAnalyticsToBigQuery(ctx context.Context, project, dataset, table string, data []*WebVital) error {
for _, d := range data {
if err := d.Validate(); err != nil {
return fmt.Errorf("validating data: %w", err)
}
}

client, err := bigquery.NewClient(ctx, project)
if err != nil {
return fmt.Errorf("connecting to bq: %w", err)
Expand All @@ -100,5 +119,31 @@ func WriteAnalyticsToBigQuery(ctx context.Context, project, dataset, table strin
}

func GetAnalytics(ctx context.Context, project, dataset, table string) ([]*WebVital, error) {
return nil, fmt.Errorf("not implemented")
client, err := bigquery.NewClient(ctx, project)
if err != nil {
return nil, fmt.Errorf("connecting to bq: %w", err)
}

t := client.Dataset(dataset).Table(table)
q := client.Query(fmt.Sprintf("SELECT * FROM `%s` AS t WHERE CAST(reports.Time as TIMESTAMP) BETWEEN TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY) AND CURRENT_TIMESTAMP() ORDER BY t.Time DESC;", t.FullyQualifiedName()))
it, err := q.Read(ctx)
if err != nil {
return nil, err
}

var ret []*WebVital
for {
var wv WebVital
err := it.Next(&wv)
if err == iterator.Done {
break
}
if err != nil {
return nil, fmt.Errorf("couldn't get WebVital: %w", err)
}

ret = append(ret, &wv)
}

return ret, nil
}
53 changes: 48 additions & 5 deletions lib/reports.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"cloud.google.com/go/bigquery"
"cloud.google.com/go/civil"
"google.golang.org/api/iterator"
)

// Report is a simple interface for types exported by ParseReport.
Expand All @@ -24,6 +25,18 @@ type Report struct {
Service bigquery.NullString
}

func (r *Report) Validate() error {
if !r.Service.Valid {
return fmt.Errorf("service is null")
}

if r.Service.StringVal == "" {
return fmt.Errorf("service is empty")
}

return nil
}

// ExpectCTReport is the struct for Expect-CT errors.
type ExpectCTReport struct {
ExpectCTReport ExpectCTSubReport `json:"expect-ct-report"`
Expand Down Expand Up @@ -104,28 +117,32 @@ func ParseReport(ct, body, srv string) (*Report, error) {
return nil, err
}

var r *Report

switch media {
case "application/reports+json":
var data []*ReportToReport
if err := json.Unmarshal([]byte(body), &data); err != nil {
return nil, err
}
return &Report{ReportTo: data, Time: now, Service: service}, nil
r = &Report{ReportTo: data, Time: now, Service: service}
case "application/expect-ct-report+json":
var data ExpectCTReport
if err := json.Unmarshal([]byte(body), &data); err != nil {
return nil, err
}
return &Report{ExpectCT: &data, Time: now, Service: service}, nil
r = &Report{ExpectCT: &data, Time: now, Service: service}
case "application/csp-report":
var data CSPReport
if err := json.Unmarshal([]byte(body), &data); err != nil {
return nil, err
}
return &Report{CSP: &data, Time: now, Service: service}, nil
r = &Report{CSP: &data, Time: now, Service: service}
default:
return nil, fmt.Errorf("%q is not a valid content-type", media)
}

return nil, fmt.Errorf("%q is not a valid content-type", media)
return r, r.Validate()
}

// UpdateReportsBQSchema updates the bigquery schema if fields are added.
Expand Down Expand Up @@ -173,5 +190,31 @@ func WriteReportToBigQuery(ctx context.Context, project, dataset, table string,
}

func GetReports(ctx context.Context, project, dataset, table string) ([]*Report, error) {
return nil, fmt.Errorf("not implemented")
client, err := bigquery.NewClient(ctx, project)
if err != nil {
return nil, fmt.Errorf("connecting to bq: %w", err)
}

t := client.Dataset(dataset).Table(table)
q := client.Query(fmt.Sprintf("SELECT * FROM `%s` AS t WHERE CAST(reports.Time as TIMESTAMP) BETWEEN TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY) AND CURRENT_TIMESTAMP() ORDER BY t.Time DESC;", t.FullyQualifiedName()))
it, err := q.Read(ctx)
if err != nil {
return nil, err
}

var ret []*Report
for {
var r Report
err := it.Next(&r)
if err == iterator.Done {
break
}
if err != nil {
return nil, fmt.Errorf("couldn't get Report: %w", err)
}

ret = append(ret, &r)
}

return ret, nil
}

0 comments on commit 89a50a5

Please sign in to comment.