go/trains: init
This commit is contained in:
parent
6d21c17a2a
commit
66875b327e
40 changed files with 4628 additions and 0 deletions
|
@ -15,6 +15,7 @@ let
|
||||||
pkgs = builtins.removeAttrs depot.nix.pkgs [ "grafana-plugins" "windows" "unifiHacked" "nixpkgs-mozilla" "nightlyRust" "nightlyRustPlatform" ];
|
pkgs = builtins.removeAttrs depot.nix.pkgs [ "grafana-plugins" "windows" "unifiHacked" "nixpkgs-mozilla" "nightlyRust" "nightlyRustPlatform" ];
|
||||||
pkg-grafana-plugins = depot.nix.pkgs.grafana-plugins;
|
pkg-grafana-plugins = depot.nix.pkgs.grafana-plugins;
|
||||||
web = lib.filterAttrs (n: v: !lib.isFunction v) depot.web;
|
web = lib.filterAttrs (n: v: !lib.isFunction v) depot.web;
|
||||||
|
trains = depot.go.trains.cmd.bins;
|
||||||
other = {
|
other = {
|
||||||
twitterchiver-archiver = depot.go.twitterchiver.archiver;
|
twitterchiver-archiver = depot.go.twitterchiver.archiver;
|
||||||
twitterchiver-archiver-docker = depot.go.twitterchiver.archiver.dockerImage;
|
twitterchiver-archiver-docker = depot.go.twitterchiver.archiver.dockerImage;
|
||||||
|
|
|
@ -7,4 +7,5 @@ args: {
|
||||||
twitternuke = import ./twitternuke args;
|
twitternuke = import ./twitternuke args;
|
||||||
minotarproxy = import ./minotarproxy args;
|
minotarproxy = import ./minotarproxy args;
|
||||||
streetworks = import ./streetworks args;
|
streetworks = import ./streetworks args;
|
||||||
|
trains = import ./trains args;
|
||||||
}
|
}
|
||||||
|
|
221
go/trains/cmd/darwiningestd/darwiningestd.go
Normal file
221
go/trains/cmd/darwiningestd/darwiningestd.go
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
awscreds "github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
awssession "github.com/aws/aws-sdk-go/aws/session"
|
||||||
|
"github.com/go-stomp/stomp/v3"
|
||||||
|
pgx "github.com/jackc/pgx/v4"
|
||||||
|
"github.com/jlaffaye/ftp"
|
||||||
|
"gocloud.dev/blob"
|
||||||
|
"gocloud.dev/blob/s3blob"
|
||||||
|
"hg.lukegb.com/lukegb/depot/go/trains/darwin"
|
||||||
|
"hg.lukegb.com/lukegb/depot/go/trains/darwin/darwindb"
|
||||||
|
"hg.lukegb.com/lukegb/depot/go/trains/darwin/darwingest"
|
||||||
|
"hg.lukegb.com/lukegb/depot/go/trains/darwin/darwingest/darwingeststomp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
cleanStart = flag.Bool("clean_start", false, "Start by downloading all timetabling and reference data and restart from snapshot")
|
||||||
|
|
||||||
|
awsAccessKey = requiredString("s3_access_key", "Darwin XML timetable S3 access key")
|
||||||
|
awsAccessSecret = requiredString("s3_access_secret", "Darwin XML timetable S3 access secret")
|
||||||
|
awsBucket = flag.String("s3_bucket", "darwin.xmltimetable", "Darwin XML timetable S3 bucket")
|
||||||
|
awsPrefix = flag.String("s3_prefix", "PPTimetable/", "Darwin XML timetable S3 bucket key prefix")
|
||||||
|
|
||||||
|
postgresString = flag.String("pg_string", "host=/var/run/postgresql database=trains search_path=darwindb", "String to connect to PG")
|
||||||
|
|
||||||
|
stompHostport = requiredString("stomp_address", "Darwin STOMP address (host:port)")
|
||||||
|
stompUsername = requiredString("stomp_username", "Darwin STOMP username")
|
||||||
|
stompPassword = requiredString("stomp_password", "Darwin STOMP password")
|
||||||
|
stompClientID = requiredString("stomp_client_id", "Darwin STOMP client ID")
|
||||||
|
stompTopic = flag.String("stomp_topic", "/topic/darwin.pushport-v16", "Darwin STOMP topic")
|
||||||
|
|
||||||
|
ftpHostport = requiredString("ftp_address", "Darwin FTP address (host:port)")
|
||||||
|
ftpUsername = requiredString("ftp_username", "Darwin FTP username")
|
||||||
|
ftpPassword = requiredString("ftp_password", "Darwin FTP password")
|
||||||
|
|
||||||
|
requiredFlags []string
|
||||||
|
)
|
||||||
|
|
||||||
|
func requiredString(name, descr string) *string {
|
||||||
|
requireFlag(name)
|
||||||
|
return flag.String(name, "", descr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func requireFlag(names ...string) {
|
||||||
|
requiredFlags = append(requiredFlags, names...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkRequiredFlags() {
|
||||||
|
for _, fname := range requiredFlags {
|
||||||
|
f := flag.CommandLine.Lookup(fname)
|
||||||
|
if f == nil {
|
||||||
|
log.Fatalf("required flag %q doesn't actually exist", fname)
|
||||||
|
}
|
||||||
|
v := f.Value.(flag.Getter).Get()
|
||||||
|
if v == nil {
|
||||||
|
log.Fatalf("required flag %q not set", fname)
|
||||||
|
}
|
||||||
|
if v, ok := v.(string); ok && v == "" {
|
||||||
|
log.Fatalf("required flag %q not set or empty", fname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
checkRequiredFlags()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
awsSess, err := awssession.NewSession(&aws.Config{
|
||||||
|
Region: aws.String("eu-west-1"),
|
||||||
|
Credentials: awscreds.NewStaticCredentials(*awsAccessKey, *awsAccessSecret, ""),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("awssession.NewSession: %w", err)
|
||||||
|
}
|
||||||
|
s3Bucket, err := s3blob.OpenBucket(ctx, awsSess, *awsBucket, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("s3blob.OpenBucket: %w", err)
|
||||||
|
}
|
||||||
|
defer s3Bucket.Close()
|
||||||
|
|
||||||
|
bucket := blob.PrefixedBucket(s3Bucket, *awsPrefix)
|
||||||
|
defer bucket.Close()
|
||||||
|
|
||||||
|
pc, err := pgx.Connect(ctx, *postgresString)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("pgx.Connect: %v", err)
|
||||||
|
}
|
||||||
|
defer pc.Close(context.Background())
|
||||||
|
|
||||||
|
catchupTx, err := pc.Begin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("beginning catchup transaction: %v", err)
|
||||||
|
}
|
||||||
|
catchupAffected := darwindb.NewAffected()
|
||||||
|
|
||||||
|
gimmeTransaction := func(ctx context.Context, cb func(ctx context.Context, tx pgx.Tx) error) error {
|
||||||
|
if catchupTx != nil {
|
||||||
|
return cb(ctx, catchupTx)
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := pc.Begin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("starting transaction: %w", err)
|
||||||
|
}
|
||||||
|
defer tx.Rollback(ctx)
|
||||||
|
|
||||||
|
if err := cb(ctx, tx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx.Commit(ctx)
|
||||||
|
}
|
||||||
|
sendAffectedNotifications := func(ctx context.Context, tx pgx.Tx, affected *darwindb.Affected) error {
|
||||||
|
for tsid := range affected.TSIDs {
|
||||||
|
if _, err := tx.Exec(ctx, fmt.Sprintf(`NOTIFY "tsid-%d"`, tsid)); err != nil {
|
||||||
|
return fmt.Errorf("notifying channel for tsid %d: %w", tsid, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
processMessage := func(ctx context.Context, pp *darwin.PushPort) error {
|
||||||
|
return gimmeTransaction(ctx, func(ctx context.Context, tx pgx.Tx) error {
|
||||||
|
affected := catchupAffected
|
||||||
|
sendAffected := false
|
||||||
|
if affected == nil {
|
||||||
|
affected = darwindb.NewAffected()
|
||||||
|
sendAffected = true
|
||||||
|
}
|
||||||
|
if err := darwindb.Process(ctx, tx, pp, affected); err != nil {
|
||||||
|
return fmt.Errorf("darwindb.Process: %w", err)
|
||||||
|
}
|
||||||
|
if sendAffected {
|
||||||
|
return sendAffectedNotifications(ctx, tx, affected)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := darwingest.Config{
|
||||||
|
Verbose: true,
|
||||||
|
DialStomp: func(ctx context.Context) (*stomp.Conn, error) {
|
||||||
|
return darwingeststomp.Dial(ctx, darwingeststomp.Config{
|
||||||
|
Hostname: *stompHostport,
|
||||||
|
Username: *stompUsername,
|
||||||
|
Password: *stompPassword,
|
||||||
|
ClientID: *stompClientID,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
StompSubscribe: func(ctx context.Context, c *stomp.Conn) (*stomp.Subscription, error) {
|
||||||
|
return c.Subscribe(*stompTopic, stomp.AckClientIndividual)
|
||||||
|
},
|
||||||
|
DialFTP: func(ctx context.Context) (*ftp.ServerConn, error) {
|
||||||
|
sc, err := ftp.Dial(*ftpHostport, ftp.DialWithContext(ctx))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("ftp.Dial: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sc.Login(*ftpUsername, *ftpPassword); err != nil {
|
||||||
|
sc.Quit()
|
||||||
|
return nil, fmt.Errorf("ftp.ServerConn.Login: %w", err)
|
||||||
|
}
|
||||||
|
return sc, nil
|
||||||
|
},
|
||||||
|
|
||||||
|
Timetable: func(ppt *darwin.PushPortTimetable) error {
|
||||||
|
return gimmeTransaction(ctx, func(ctx context.Context, tx pgx.Tx) error {
|
||||||
|
if err := darwindb.ProcessTimetable(ctx, tx, ppt, catchupAffected); err != nil {
|
||||||
|
return fmt.Errorf("processing timetable data: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
},
|
||||||
|
ReferenceData: func(pprd *darwin.PushPortReferenceData) error {
|
||||||
|
return gimmeTransaction(ctx, func(ctx context.Context, tx pgx.Tx) error {
|
||||||
|
if err := darwindb.ProcessReferenceData(ctx, tx, pprd, catchupAffected); err != nil {
|
||||||
|
return fmt.Errorf("processing reference data: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
},
|
||||||
|
MessageCatchUp: func(pp *darwin.PushPort) error {
|
||||||
|
return processMessage(ctx, pp)
|
||||||
|
},
|
||||||
|
CatchUpComplete: func(ctx context.Context) error {
|
||||||
|
log.Printf("catchup affected: %v", catchupAffected.Summary())
|
||||||
|
if err := sendAffectedNotifications(ctx, catchupTx, catchupAffected); err != nil {
|
||||||
|
return fmt.Errorf("sending affected notifications: %v", err)
|
||||||
|
}
|
||||||
|
if err := catchupTx.Commit(ctx); err != nil {
|
||||||
|
return fmt.Errorf("committing catchup transaction: %v", err)
|
||||||
|
}
|
||||||
|
catchupAffected = nil
|
||||||
|
catchupTx = nil
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Message: func(pp *darwin.PushPort) error {
|
||||||
|
return processMessage(ctx, pp)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if *cleanStart {
|
||||||
|
if err := darwingest.CatchUpAndStart(ctx, bucket, cfg); err != nil {
|
||||||
|
log.Printf("CatchUpAndStart: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := darwingest.Start(ctx, bucket, cfg); err != nil {
|
||||||
|
log.Printf("Start: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("terminating...")
|
||||||
|
}
|
24
go/trains/cmd/darwiningestd/default.nix
Normal file
24
go/trains/cmd/darwiningestd/default.nix
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }:
|
||||||
|
|
||||||
|
depot.third_party.buildGo.program {
|
||||||
|
name = "darwiningestd";
|
||||||
|
srcs = [ ./darwiningestd.go ];
|
||||||
|
deps = with depot.third_party; [
|
||||||
|
gopkgs."github.com".aws.aws-sdk-go.aws
|
||||||
|
gopkgs."github.com".aws.aws-sdk-go.aws.credentials
|
||||||
|
gopkgs."github.com".aws.aws-sdk-go.aws.session
|
||||||
|
gopkgs."github.com".go-stomp.stomp.v3
|
||||||
|
gopkgs."github.com".jackc.pgx.v4
|
||||||
|
gopkgs."github.com".jlaffaye.ftp
|
||||||
|
gopkgs."gocloud.dev".blob
|
||||||
|
gopkgs."gocloud.dev".blob.s3blob
|
||||||
|
depot.go.trains.darwin
|
||||||
|
depot.go.trains.darwin.darwindb
|
||||||
|
depot.go.trains.darwin.darwingest
|
||||||
|
depot.go.trains.darwin.darwingest.darwingeststomp
|
||||||
|
];
|
||||||
|
}
|
463
go/trains/cmd/db2web/db2web.go
Normal file
463
go/trains/cmd/db2web/db2web.go
Normal file
|
@ -0,0 +1,463 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v4"
|
||||||
|
"github.com/jackc/pgx/v4/pgxpool"
|
||||||
|
"hg.lukegb.com/lukegb/depot/go/trains/webapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
type querier interface {
|
||||||
|
Query(ctx context.Context, sql string, args ...interface{}) (pgx.Rows, error)
|
||||||
|
QueryRow(ctx context.Context, sql string, args ...interface{}) pgx.Row
|
||||||
|
}
|
||||||
|
|
||||||
|
func summarizeService(ctx context.Context, pc querier, id int) (*webapi.ServiceData, error) {
|
||||||
|
row := pc.QueryRow(ctx, `
|
||||||
|
SELECT
|
||||||
|
ts.id, ts.rid, ts.uid, ts.rsid, ts.headcode, ts.scheduled_start_date::varchar,
|
||||||
|
ts.train_operator, rop.name, rop.url,
|
||||||
|
COALESCE(ts.delay_reason_code::int, 0), COALESCE(rlr.text, ''), COALESCE(ts.delay_reason_tiploc, ''), COALESCE(rlrloc.name, ''), rlrloc.crs, COALESCE(rlrloc.toc, ''), rlrop.name, rlrop.url, COALESCE(ts.delay_reason_tiploc_near, false),
|
||||||
|
COALESCE(ts.cancellation_reason_code::int, 0), COALESCE(rlr.text, ''), COALESCE(ts.cancellation_reason_tiploc, ''), COALESCE(rlrloc.name, ''), rlrloc.crs, COALESCE(rlrloc.toc, ''), rlrop.name, rlrop.url, COALESCE(ts.cancellation_reason_tiploc_near, false),
|
||||||
|
ts.active, ts.deleted, ts.cancelled
|
||||||
|
FROM
|
||||||
|
train_services ts
|
||||||
|
LEFT JOIN ref_tocs rop ON rop.toc=ts.train_operator
|
||||||
|
|
||||||
|
LEFT JOIN ref_late_running_reasons rlr ON rlr.code=ts.delay_reason_code::int
|
||||||
|
LEFT JOIN ref_locations rlrloc ON rlrloc.tiploc=ts.delay_reason_tiploc
|
||||||
|
LEFT JOIN ref_tocs rlrop ON rlrop.toc=rlrloc.toc
|
||||||
|
|
||||||
|
LEFT JOIN ref_cancel_reasons rc ON rc.code=ts.cancellation_reason_code::int
|
||||||
|
LEFT JOIN ref_locations rcloc ON rcloc.tiploc=ts.cancellation_reason_tiploc
|
||||||
|
LEFT JOIN ref_tocs rcop ON rcop.toc=rcloc.toc
|
||||||
|
WHERE
|
||||||
|
ts.id=$1
|
||||||
|
`, id)
|
||||||
|
|
||||||
|
sd := webapi.ServiceData{
|
||||||
|
DelayReason: &webapi.DisruptionReason{Location: &webapi.Location{TOC: &webapi.TrainOperator{}}},
|
||||||
|
CancelReason: &webapi.DisruptionReason{Location: &webapi.Location{TOC: &webapi.TrainOperator{}}},
|
||||||
|
}
|
||||||
|
if err := row.Scan(
|
||||||
|
&sd.ID, &sd.RID, &sd.UID, &sd.RSID, &sd.Headcode, &sd.StartDate,
|
||||||
|
&sd.TrainOperator.Code, &sd.TrainOperator.Name, &sd.TrainOperator.URL,
|
||||||
|
&sd.DelayReason.Code, &sd.DelayReason.Text, &sd.DelayReason.Location.TIPLOC, &sd.DelayReason.Location.Name, &sd.DelayReason.Location.CRS, &sd.DelayReason.Location.TOC.Code, &sd.DelayReason.Location.TOC.Name, &sd.DelayReason.Location.TOC.URL, &sd.DelayReason.NearLocation,
|
||||||
|
&sd.CancelReason.Code, &sd.CancelReason.Text, &sd.CancelReason.Location.TIPLOC, &sd.CancelReason.Location.Name, &sd.CancelReason.Location.CRS, &sd.CancelReason.Location.TOC.Code, &sd.CancelReason.Location.TOC.Name, &sd.CancelReason.Location.TOC.URL, &sd.CancelReason.NearLocation,
|
||||||
|
&sd.Active, &sd.Deleted, &sd.Cancelled,
|
||||||
|
); err != nil {
|
||||||
|
return nil, fmt.Errorf("reading from train_services for %d: %w", id, err)
|
||||||
|
}
|
||||||
|
sd.Canonicalize()
|
||||||
|
|
||||||
|
// Now for the locations.
|
||||||
|
rows, err := pc.Query(ctx, `
|
||||||
|
SELECT
|
||||||
|
tl.id,
|
||||||
|
tl.tiploc, COALESCE(rloc.name, ''), COALESCE(rloc.crs, ''), COALESCE(rloc.toc, ''), rloctoc.name, rloctoc.url,
|
||||||
|
tl.calling_point::varchar, tl.train_length, tl.service_suppressed,
|
||||||
|
tl.schedule_cancelled,
|
||||||
|
COALESCE(tl.schedule_platform, ''), COALESCE(tl.platform, ''), COALESCE(tl.platform_confirmed, false), COALESCE(tl.platform_suppressed, false),
|
||||||
|
tl.schedule_false_destination_tiploc, COALESCE(rfdloc.name, ''), COALESCE(rfdloc.crs, ''), COALESCE(rfdloc.toc, ''), rfdloctoc.name, rfdloctoc.url,
|
||||||
|
|
||||||
|
tl.schedule_public_arrival, tl.schedule_working_arrival, tl.estimated_arrival, tl.working_estimated_arrival, tl.actual_arrival,
|
||||||
|
tl.schedule_public_departure, tl.schedule_working_departure, tl.estimated_departure, tl.working_estimated_departure, tl.actual_departure,
|
||||||
|
/* no public_pass */ tl.schedule_working_pass, tl.estimated_pass, tl.working_estimated_pass, tl.actual_pass
|
||||||
|
FROM
|
||||||
|
train_locations tl
|
||||||
|
LEFT JOIN ref_locations rloc ON rloc.tiploc=tl.tiploc
|
||||||
|
LEFT JOIN ref_tocs rloctoc ON rloctoc.toc=rloc.toc
|
||||||
|
|
||||||
|
LEFT JOIN ref_locations rfdloc ON rfdloc.tiploc=tl.schedule_false_destination_tiploc
|
||||||
|
LEFT JOIN ref_tocs rfdloctoc ON rfdloctoc.toc=rfdloc.toc
|
||||||
|
WHERE
|
||||||
|
tl.tsid=$1 AND tl.active_in_schedule
|
||||||
|
ORDER BY
|
||||||
|
COALESCE(tl.schedule_working_arrival, tl.schedule_working_pass, tl.schedule_working_departure)
|
||||||
|
`, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("querying locations for %d: %w", id, err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
loc := webapi.ServiceLocation{
|
||||||
|
Location: &webapi.Location{TOC: &webapi.TrainOperator{}},
|
||||||
|
Platform: &webapi.PlatformData{},
|
||||||
|
FalseDestination: &webapi.Location{TOC: &webapi.TrainOperator{}},
|
||||||
|
ArrivalTiming: &webapi.TimingData{},
|
||||||
|
DepartureTiming: &webapi.TimingData{},
|
||||||
|
PassTiming: &webapi.TimingData{},
|
||||||
|
}
|
||||||
|
if err := rows.Scan(
|
||||||
|
&loc.ID,
|
||||||
|
&loc.Location.TIPLOC, &loc.Location.Name, &loc.Location.CRS, &loc.Location.TOC.Code, &loc.Location.TOC.Name, &loc.Location.TOC.URL,
|
||||||
|
&loc.CallingPointType, &loc.Length, &loc.Suppressed,
|
||||||
|
&loc.Cancelled,
|
||||||
|
&loc.Platform.Scheduled, &loc.Platform.Live, &loc.Platform.Confirmed, &loc.Platform.Suppressed,
|
||||||
|
&loc.FalseDestination.TIPLOC, &loc.FalseDestination.Name, &loc.FalseDestination.CRS, &loc.FalseDestination.TOC.Code, &loc.FalseDestination.TOC.Name, &loc.FalseDestination.TOC.URL,
|
||||||
|
&loc.ArrivalTiming.PublicScheduled, &loc.ArrivalTiming.WorkingScheduled, &loc.ArrivalTiming.PublicEstimated, &loc.ArrivalTiming.WorkingEstimated, &loc.ArrivalTiming.Actual,
|
||||||
|
&loc.DepartureTiming.PublicScheduled, &loc.DepartureTiming.WorkingScheduled, &loc.DepartureTiming.PublicEstimated, &loc.DepartureTiming.WorkingEstimated, &loc.DepartureTiming.Actual,
|
||||||
|
/*&loc.PassTiming.PublicScheduled,*/ &loc.PassTiming.WorkingScheduled, &loc.PassTiming.PublicEstimated, &loc.PassTiming.WorkingEstimated, &loc.PassTiming.Actual,
|
||||||
|
); err != nil {
|
||||||
|
return nil, fmt.Errorf("scanning locations for %d: %w", id, err)
|
||||||
|
}
|
||||||
|
loc.Canonicalize()
|
||||||
|
sd.Locations = append(sd.Locations, loc)
|
||||||
|
}
|
||||||
|
if rows.Err() != nil {
|
||||||
|
return nil, fmt.Errorf("iterating over locations for %d: %w", id, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &sd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type watchingResponseWriter struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
WrittenTo bool
|
||||||
|
WroteStatus int
|
||||||
|
WroteContentBytes int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *watchingResponseWriter) Flush() {
|
||||||
|
if rw.Flusher != nil {
|
||||||
|
rw.Flusher.Flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *watchingResponseWriter) Header() http.Header { return rw.ResponseWriter.Header() }
|
||||||
|
func (rw *watchingResponseWriter) Write(bs []byte) (int, error) {
|
||||||
|
if !rw.WrittenTo {
|
||||||
|
rw.WroteStatus = http.StatusOK
|
||||||
|
}
|
||||||
|
rw.WrittenTo = true
|
||||||
|
rw.WroteContentBytes += len(bs)
|
||||||
|
return rw.ResponseWriter.Write(bs)
|
||||||
|
}
|
||||||
|
func (rw *watchingResponseWriter) WriteHeader(statusCode int) {
|
||||||
|
if rw.WrittenTo {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rw.WrittenTo = true
|
||||||
|
rw.WroteStatus = statusCode
|
||||||
|
rw.ResponseWriter.WriteHeader(statusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpError struct {
|
||||||
|
err error
|
||||||
|
httpStatusCode int
|
||||||
|
publicError string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (he httpError) Error() string {
|
||||||
|
if he.err == nil {
|
||||||
|
return fmt.Sprintf("HTTP %d: %v", he.httpStatusCode, he.publicError)
|
||||||
|
}
|
||||||
|
return he.err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
type server struct {
|
||||||
|
dbPool *pgxpool.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
var jsonPathRegexp = regexp.MustCompile(`/([1-9][0-9]*)$`)
|
||||||
|
|
||||||
|
func (s *server) handleJSON(ctx context.Context, rw http.ResponseWriter, r *http.Request) error {
|
||||||
|
// /<id>
|
||||||
|
ms := jsonPathRegexp.FindStringSubmatch(r.URL.Path)
|
||||||
|
if len(ms) != 2 {
|
||||||
|
return httpError{
|
||||||
|
httpStatusCode: http.StatusNotFound,
|
||||||
|
publicError: "not found",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
id, err := strconv.Atoi(ms[1])
|
||||||
|
if err != nil {
|
||||||
|
return httpError{
|
||||||
|
err: err,
|
||||||
|
httpStatusCode: http.StatusBadRequest,
|
||||||
|
publicError: "bad id",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := s.dbPool.Acquire(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("acquiring database connection: %w", err)
|
||||||
|
}
|
||||||
|
defer conn.Release()
|
||||||
|
|
||||||
|
svc, err := summarizeService(ctx, conn, id)
|
||||||
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
|
return httpError{
|
||||||
|
err: err,
|
||||||
|
httpStatusCode: http.StatusNotFound,
|
||||||
|
publicError: "no train with that ID",
|
||||||
|
}
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rw.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
if err := json.NewEncoder(rw).Encode(svc); err != nil {
|
||||||
|
return fmt.Errorf("encoding JSON: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var channelTIDRegex = regexp.MustCompile(`tsid-([0-9]+)`)
|
||||||
|
|
||||||
|
func (s *server) handleEventStream(ctx context.Context, rw http.ResponseWriter, r *http.Request) error {
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if r.URL.Path != "/" {
|
||||||
|
return httpError{
|
||||||
|
httpStatusCode: http.StatusNotFound,
|
||||||
|
publicError: "not found",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qs, err := url.ParseQuery(r.URL.RawQuery)
|
||||||
|
if err != nil {
|
||||||
|
return httpError{
|
||||||
|
err: err,
|
||||||
|
httpStatusCode: http.StatusBadRequest,
|
||||||
|
publicError: "can't parse query string",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ids []int
|
||||||
|
for _, idStr := range qs["id"] {
|
||||||
|
id, err := strconv.Atoi(idStr)
|
||||||
|
if err != nil {
|
||||||
|
return httpError{
|
||||||
|
err: err,
|
||||||
|
httpStatusCode: http.StatusBadRequest,
|
||||||
|
publicError: "id parameter bad",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return httpError{
|
||||||
|
httpStatusCode: http.StatusBadRequest,
|
||||||
|
publicError: "need at least one ?id= to watch",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listenConn, err := s.dbPool.Acquire(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("acquiring database connection: %w", err)
|
||||||
|
}
|
||||||
|
defer listenConn.Release()
|
||||||
|
|
||||||
|
for _, id := range ids {
|
||||||
|
_, err := listenConn.Exec(ctx, fmt.Sprintf(`LISTEN "tsid-%d"`, id))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("watching for %d: %w", id, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rw.Header().Set("Content-Type", "text/event-stream")
|
||||||
|
rw.Header().Set("Cache-Control", "no-cache")
|
||||||
|
|
||||||
|
je := json.NewEncoder(rw)
|
||||||
|
|
||||||
|
encodeSvc := func(conn querier, id int) error {
|
||||||
|
svc, err := summarizeService(ctx, conn, id)
|
||||||
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
|
if _, err := fmt.Fprintf(rw, "event: notyet\ndata: %d\n\n", id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if err != nil {
|
||||||
|
return fmt.Errorf("summarizeService(%d): %w", id, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := fmt.Fprint(rw, "data: "); err != nil {
|
||||||
|
return fmt.Errorf("writing data: prefix for service %d: %w", id, err)
|
||||||
|
}
|
||||||
|
if err := je.Encode(svc); err != nil {
|
||||||
|
return fmt.Errorf("je.Encode service %d: %w", id, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := fmt.Fprint(rw, "\n\n"); err != nil {
|
||||||
|
return fmt.Errorf("printing separator after service %d: %w", id, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if f, ok := rw.(http.Flusher); ok {
|
||||||
|
f.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, id := range ids {
|
||||||
|
if err := encodeSvc(listenConn, id); err != nil {
|
||||||
|
return fmt.Errorf("initial data for service %d: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type updatedMsg struct {
|
||||||
|
TID int
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
updatedCh := make(chan updatedMsg)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(updatedCh)
|
||||||
|
for {
|
||||||
|
n, err := listenConn.Conn().WaitForNotification(ctx)
|
||||||
|
if err != nil {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case updatedCh <- updatedMsg{Err: err}:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ms := channelTIDRegex.FindStringSubmatch(n.Channel)
|
||||||
|
if len(ms) != 2 {
|
||||||
|
log.Printf("unrecognised notification channel %q", n.Channel)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := strconv.Atoi(ms[1])
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to parse TID from notification channel %q: %v", n.Channel, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case updatedCh <- updatedMsg{TID: id}:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
pingCh := time.NewTicker(1 * time.Minute)
|
||||||
|
defer pingCh.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-pingCh.C:
|
||||||
|
if _, err := fmt.Fprint(rw, "event: ping\n\n"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if f, ok := rw.(http.Flusher); ok {
|
||||||
|
f.Flush()
|
||||||
|
}
|
||||||
|
case umsg, ok := <-updatedCh:
|
||||||
|
if !ok {
|
||||||
|
log.Printf("updatedCh closed, shutting down conn")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if umsg.Err != nil {
|
||||||
|
return umsg.Err
|
||||||
|
}
|
||||||
|
sconn, err := s.dbPool.Acquire(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("acquiring database connection: %w", err)
|
||||||
|
}
|
||||||
|
err = encodeSvc(sconn, umsg.TID)
|
||||||
|
sconn.Release()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) handleHTTP(ctx context.Context, rw http.ResponseWriter, r *http.Request) error {
|
||||||
|
accept := r.Header.Get("Accept")
|
||||||
|
switch accept {
|
||||||
|
case "application/json":
|
||||||
|
return s.handleJSON(ctx, rw, r)
|
||||||
|
case "text/event-stream":
|
||||||
|
return s.handleEventStream(ctx, rw, r)
|
||||||
|
default:
|
||||||
|
return httpError{
|
||||||
|
httpStatusCode: http.StatusNotAcceptable,
|
||||||
|
publicError: "Accept should be text/event-stream or application/json",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
wrw := &watchingResponseWriter{ResponseWriter: rw, Flusher: rw.(http.Flusher)}
|
||||||
|
if err := s.handleHTTP(r.Context(), wrw, r); err != nil {
|
||||||
|
statusCode := http.StatusInternalServerError
|
||||||
|
errText := fmt.Sprintf("internal server error: %v", err.Error())
|
||||||
|
|
||||||
|
var he httpError
|
||||||
|
if errors.As(err, &he) {
|
||||||
|
if he.httpStatusCode != 0 {
|
||||||
|
statusCode = he.httpStatusCode
|
||||||
|
}
|
||||||
|
if he.publicError != "" {
|
||||||
|
errText = he.publicError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Printf("[%v] %v %v -- %v (%d %v)", r.RemoteAddr, r.Method, r.RequestURI, err, statusCode, errText)
|
||||||
|
|
||||||
|
if !wrw.WrittenTo {
|
||||||
|
|
||||||
|
for k := range wrw.Header() {
|
||||||
|
wrw.Header().Del(k)
|
||||||
|
}
|
||||||
|
wrw.Header().Set("Content-Type", "text/plain")
|
||||||
|
wrw.WriteHeader(statusCode)
|
||||||
|
fmt.Fprintf(wrw, "%s\n", errText)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("[%v] %v %v -- (%d) %d bytes", r.RemoteAddr, r.Method, r.RequestURI, wrw.WroteStatus, wrw.WroteContentBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cfg, err := pgxpool.ParseConfig("host=/var/run/postgresql database=trains search_path=darwindb")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("pgxpool.ParseConfig: %v", err)
|
||||||
|
}
|
||||||
|
cfg.AfterRelease = func(pc *pgx.Conn) bool {
|
||||||
|
// true to return to pool, false to destroy
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
_, err := pc.Exec(ctx, "UNLISTEN *")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("pg connection cleanup: UNLISTEN failed: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
pcpool, err := pgxpool.ConnectConfig(ctx, cfg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("pgxpool.ConnectConfig: %v", err)
|
||||||
|
}
|
||||||
|
defer pcpool.Close()
|
||||||
|
|
||||||
|
s := &server{
|
||||||
|
dbPool: pcpool,
|
||||||
|
}
|
||||||
|
listen := ":13974"
|
||||||
|
log.Printf("listening on %s", listen)
|
||||||
|
log.Fatal(http.ListenAndServe(listen, s))
|
||||||
|
}
|
15
go/trains/cmd/db2web/default.nix
Normal file
15
go/trains/cmd/db2web/default.nix
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }:
|
||||||
|
|
||||||
|
depot.third_party.buildGo.program {
|
||||||
|
name = "db2web";
|
||||||
|
srcs = [ ./db2web.go ];
|
||||||
|
deps = with depot.third_party; [
|
||||||
|
gopkgs."github.com".jackc.pgx.v4
|
||||||
|
gopkgs."github.com".jackc.pgx.v4.pgxpool
|
||||||
|
depot.go.trains.webapi
|
||||||
|
];
|
||||||
|
}
|
17
go/trains/cmd/default.nix
Normal file
17
go/trains/cmd/default.nix
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }@args:
|
||||||
|
|
||||||
|
rec {
|
||||||
|
darwiningestd = import ./darwiningestd args;
|
||||||
|
db2web = import ./db2web args;
|
||||||
|
rttingest = import ./rttingest args;
|
||||||
|
train2livesplit = import ./train2livesplit args;
|
||||||
|
|
||||||
|
bins = {
|
||||||
|
inherit darwiningestd db2web train2livesplit;
|
||||||
|
inherit (rttingest) tiploc;
|
||||||
|
};
|
||||||
|
}
|
15
go/trains/cmd/rttingest/default.nix
Normal file
15
go/trains/cmd/rttingest/default.nix
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
tiploc = depot.third_party.buildGo.program {
|
||||||
|
name = "tiploc";
|
||||||
|
srcs = [ ./tiploc.go ];
|
||||||
|
deps = with depot.third_party; [
|
||||||
|
gopkgs."github.com".jackc.pgx.v4
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
131
go/trains/cmd/rttingest/tiploc.go
Normal file
131
go/trains/cmd/rttingest/tiploc.go
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
pgx "github.com/jackc/pgx/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
func fetchWork(ctx context.Context, pc *pgx.Conn) ([]string, error) {
|
||||||
|
rows, err := pc.Query(ctx, `SELECT tiploc FROM ref_locations WHERE name=tiploc AND override_name IS NULL`)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("querying for work: %w", err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var tiplocs []string
|
||||||
|
for rows.Next() {
|
||||||
|
var tiploc string
|
||||||
|
if err := rows.Scan(&tiploc); err != nil {
|
||||||
|
return nil, fmt.Errorf("scanning row: %w", err)
|
||||||
|
}
|
||||||
|
tiplocs = append(tiplocs, tiploc)
|
||||||
|
}
|
||||||
|
if rows.Err() != nil {
|
||||||
|
return nil, fmt.Errorf("retrieving rows for work: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tiplocs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type RTT struct {
|
||||||
|
Username, Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RTTLocationDetail struct {
|
||||||
|
TIPLOC string `json:"tiploc"`
|
||||||
|
CRS string `json:"crs"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RTTLocationContainer struct {
|
||||||
|
Location *RTTLocationDetail `json:"location"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r RTT) LocationDetail(ctx context.Context, inp string) (*RTTLocationDetail, error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.rtt.io/api/v1/json/search/%v", inp), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("formulating query for %q: %w", inp, err)
|
||||||
|
}
|
||||||
|
req.SetBasicAuth(r.Username, r.Password)
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("querying API for %q: %w", inp, err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("status for %q was %d %v; wanted 200 OK", inp, resp.StatusCode, resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cont RTTLocationContainer
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&cont); err != nil {
|
||||||
|
return nil, fmt.Errorf("unmarshalling API response for %q: %w", inp, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cont.Location, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
pc, err := pgx.Connect(ctx, "host=/var/run/postgresql database=trains search_path=darwindb")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("pgx.Connect: %v", err)
|
||||||
|
}
|
||||||
|
defer pc.Close(context.Background())
|
||||||
|
|
||||||
|
tiplocs, err := fetchWork(ctx, pc)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("fetchWork: %v", err)
|
||||||
|
}
|
||||||
|
log.Printf("%d tiplocs to process", len(tiplocs))
|
||||||
|
|
||||||
|
r := RTT{Username: "rttapi_lukegb", Password: "5c1e5ae8a88882b090cd2e55225a3f0f581ec57c"}
|
||||||
|
|
||||||
|
var rttApiErrCount int
|
||||||
|
for n, t := range tiplocs {
|
||||||
|
if rttApiErrCount == 20 {
|
||||||
|
log.Println("Too many errors from RTT, stopping")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if n%20 == 0 {
|
||||||
|
log.Printf("%d/%d (%d%%)", n, len(tiplocs), (n*100)/len(tiplocs))
|
||||||
|
}
|
||||||
|
|
||||||
|
d, err := r.LocationDetail(ctx, t)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("LocationDetail(ctx, %q): %v", t, err)
|
||||||
|
rttApiErrCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if d == nil {
|
||||||
|
log.Printf("got a nil location for %q!", t)
|
||||||
|
rttApiErrCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rttApiErrCount = 0 // reset the breaker
|
||||||
|
if d.Name == "" {
|
||||||
|
log.Printf("No name for %q", t)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ct, err := pc.Exec(ctx, `UPDATE ref_locations SET name=$2, override_name=$2, override_name_src='RTT' WHERE tiploc=$1 AND override_name IS NULL AND name=tiploc`, t, d.Name)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Updating database for %q failed: %w", t, err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if ra := ct.RowsAffected(); ra != 1 {
|
||||||
|
log.Printf("Updated %d rows for %q; wanted 1???", ra, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("terminating...")
|
||||||
|
}
|
12
go/trains/cmd/train2livesplit/default.nix
Normal file
12
go/trains/cmd/train2livesplit/default.nix
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }:
|
||||||
|
depot.third_party.buildGo.program {
|
||||||
|
name = "train2livesplit";
|
||||||
|
srcs = [ ./train2livesplit.go ];
|
||||||
|
deps = with depot.third_party; [
|
||||||
|
depot.go.trains.webapi
|
||||||
|
];
|
||||||
|
}
|
247
go/trains/cmd/train2livesplit/train2livesplit.go
Normal file
247
go/trains/cmd/train2livesplit/train2livesplit.go
Normal file
|
@ -0,0 +1,247 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"hg.lukegb.com/lukegb/depot/go/trains/webapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
header = template.Must(template.New("script").Parse(`
|
||||||
|
startscript C#
|
||||||
|
this["model"].Reset();
|
||||||
|
var run = this["state"].Run;
|
||||||
|
run.CategoryName = "{{if .Service.Headcode}}{{.Service.Headcode}} {{end}}{{.FirstLocation.DepartureTiming.PublicScheduled.Format "1504"}} {{.FirstLocation.Location.Name}} to {{.LastLocation.Location.Name}}";
|
||||||
|
run.GameName = "{{.Service.StartDate}}";
|
||||||
|
run.AttemptCount = 0;
|
||||||
|
run.AttemptHistory.Clear();
|
||||||
|
run.AutoSplitter = null;
|
||||||
|
run.HasChanged = true;
|
||||||
|
run.Clear();
|
||||||
|
|
||||||
|
var ass = run.GetType().Assembly;
|
||||||
|
var timingMethodRealTime = Enum.GetValues(ass.GetType("LiveSplit.Model.TimingMethod"))[0];
|
||||||
|
this["state"].CurrentTimingMethod = timingMethodRealTime;
|
||||||
|
|
||||||
|
var timeStampType = ass.GetType("LiveSplit.Model.TimeStamp");
|
||||||
|
var atomicDateTime = ass.GetType("LiveSplit.Model.AtomicDateTime"); // (System.DateTime, synced)
|
||||||
|
|
||||||
|
var runExtensionsType = ass.GetType("LiveSplit.Model.RunExtensions");
|
||||||
|
var runExtensionsAddSegment = runExtensionsType.GetMethod("AddSegment");
|
||||||
|
|
||||||
|
var timeType = ass.GetType("LiveSplit.Model.Time");
|
||||||
|
var defaultTime = Activator.CreateInstance(timeType);
|
||||||
|
var lastSplitTime = defaultTime;
|
||||||
|
|
||||||
|
Func<int, object> timeFromSeconds = delegate (int seconds) {
|
||||||
|
if (seconds < 0) {
|
||||||
|
return defaultTime;
|
||||||
|
}
|
||||||
|
var time = Activator.CreateInstance(timeType);
|
||||||
|
time.RealTime = TimeSpan.FromSeconds(seconds);
|
||||||
|
return time;
|
||||||
|
};
|
||||||
|
Action<string, int, int> addSplit = delegate (string name, int timetableSeconds, int realSeconds) {
|
||||||
|
var timetableTime = Convert.ChangeType(timeFromSeconds(timetableSeconds), timeType);
|
||||||
|
var lastTimetableTime = Convert.ChangeType(timeFromSeconds(0), timeType);
|
||||||
|
if (this["state"].Run.Count > 0) {
|
||||||
|
lastTimetableTime = this["state"].Run[this["state"].Run.Count-1].PersonalBestSplitTime;
|
||||||
|
}
|
||||||
|
var timetableSegmentTime = timetableTime - lastTimetableTime;
|
||||||
|
var realTime = timeFromSeconds(realSeconds);
|
||||||
|
runExtensionsAddSegment.Invoke(null, new object[] { run, name, timetableTime, timetableSegmentTime, null, realTime, null });
|
||||||
|
lastSplitTime = this["state"].Run[this["state"].Run.Count-1].SplitTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
// run, name, pbSplitTime, bestSegmentTime, icon, splitTime, segmentHistory
|
||||||
|
{{.AddSplitFunctions}}
|
||||||
|
endscript
|
||||||
|
`))
|
||||||
|
|
||||||
|
trainId = flag.Int("train_id", -1, "Train ID")
|
||||||
|
endpoint = flag.String("endpoint", "http://localhost:13974", "db2web endpoint")
|
||||||
|
)
|
||||||
|
|
||||||
|
type templateVars struct {
|
||||||
|
Service *webapi.ServiceData
|
||||||
|
FirstLocation *webapi.ServiceLocation
|
||||||
|
LastLocation *webapi.ServiceLocation
|
||||||
|
AddSplitFunctions string
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchTrainOnce(ctx context.Context, id int) (*webapi.ServiceData, error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/%d", *endpoint, id), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("constructing new request for %d: %w", id, err)
|
||||||
|
}
|
||||||
|
req.Header.Set("Accept", "application/json")
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("doing request for %d: %w", id, err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("http status for %d was %d %v", id, resp.StatusCode, resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
var sd webapi.ServiceData
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&sd); err != nil {
|
||||||
|
return nil, fmt.Errorf("decoding train %d: %w", id, err)
|
||||||
|
}
|
||||||
|
return &sd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateLiveSplitScriptData(sd *webapi.ServiceData) (*templateVars, error) {
|
||||||
|
asf := new(strings.Builder)
|
||||||
|
currentSplit := -1
|
||||||
|
usingTime := func(td *webapi.TimingData) time.Time {
|
||||||
|
if td.PublicScheduled != nil {
|
||||||
|
return *td.PublicScheduled
|
||||||
|
}
|
||||||
|
return *td.WorkingScheduled
|
||||||
|
}
|
||||||
|
timetableDepartureTime := usingTime(sd.Locations[0].DepartureTiming)
|
||||||
|
var lastHadActual bool
|
||||||
|
for n, l := range sd.Locations {
|
||||||
|
var usingData *webapi.TimingData
|
||||||
|
switch {
|
||||||
|
case l.DepartureTiming != nil:
|
||||||
|
usingData = l.DepartureTiming
|
||||||
|
case l.PassTiming != nil:
|
||||||
|
usingData = l.PassTiming
|
||||||
|
case l.ArrivalTiming != nil:
|
||||||
|
usingData = l.ArrivalTiming
|
||||||
|
}
|
||||||
|
usingTimetableTime := usingTime(usingData)
|
||||||
|
|
||||||
|
var secondsOnTimetable, secondsActual int
|
||||||
|
secondsOnTimetable = int((usingTimetableTime.Sub(timetableDepartureTime)) / time.Second)
|
||||||
|
secondsActual = -1
|
||||||
|
|
||||||
|
if lastHadActual {
|
||||||
|
currentSplit = n
|
||||||
|
lastHadActual = false
|
||||||
|
}
|
||||||
|
if usingData.Actual != nil {
|
||||||
|
secondsActual = int((usingData.Actual.Sub(timetableDepartureTime)) / time.Second)
|
||||||
|
lastHadActual = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(asf, "addSplit(%q, %d, %d);\n", l.Location.Name, secondsOnTimetable, secondsActual)
|
||||||
|
}
|
||||||
|
if lastHadActual {
|
||||||
|
fmt.Fprintf(asf, `
|
||||||
|
this["state"].Run.Offset = TimeSpan.FromSeconds(%d);
|
||||||
|
this["model"].Start();
|
||||||
|
this["state"].CurrentSplitIndex = %d-1;
|
||||||
|
this["model"].Split();
|
||||||
|
this["state"].Run[%d-1].SplitTime = lastSplitTime;
|
||||||
|
`, (time.Now().Sub(timetableDepartureTime))/time.Second, len(sd.Locations), len(sd.Locations))
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(asf, `
|
||||||
|
this["state"].Run.Offset = TimeSpan.FromSeconds(%d);
|
||||||
|
this["model"].Start();
|
||||||
|
this["state"].CurrentSplitIndex = %d;
|
||||||
|
`, (time.Now().Sub(timetableDepartureTime))/time.Second, currentSplit)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &templateVars{
|
||||||
|
Service: sd,
|
||||||
|
FirstLocation: &sd.Locations[0],
|
||||||
|
LastLocation: &sd.Locations[len(sd.Locations)-1],
|
||||||
|
AddSplitFunctions: asf.String(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchTrainAndOutputLiveSplit(ctx context.Context, id int) error {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/?id=%d", *endpoint, id), nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("constructing new request for %d: %w", id, err)
|
||||||
|
}
|
||||||
|
req.Header.Set("Accept", "text/event-stream")
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("doing request for %d: %w", id, err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return fmt.Errorf("http status for %d was %d %v", id, resp.StatusCode, resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
sc := bufio.NewScanner(resp.Body)
|
||||||
|
var buffer []string
|
||||||
|
for sc.Scan() {
|
||||||
|
buffer = append(buffer, sc.Text())
|
||||||
|
if buffer[0] == "" {
|
||||||
|
buffer = buffer[1:]
|
||||||
|
}
|
||||||
|
fmt.Fprintf(os.Stderr, "%#v\n\n", buffer)
|
||||||
|
if len(buffer) < 2 || buffer[len(buffer)-1] != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(buffer[0], "event:") {
|
||||||
|
buffer = nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var xs []string
|
||||||
|
for _, s := range buffer {
|
||||||
|
xs = append(xs, strings.TrimPrefix(s, "data: "))
|
||||||
|
}
|
||||||
|
|
||||||
|
var sd webapi.ServiceData
|
||||||
|
if err := json.Unmarshal([]byte(strings.Join(xs, "")), &sd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tv, err := generateLiveSplitScriptData(&sd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := header.Execute(os.Stdout, tv); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
log.Fatal(fetchTrainAndOutputLiveSplit(ctx, *trainId))
|
||||||
|
|
||||||
|
sd, err := fetchTrainOnce(ctx, 33634)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
tv, err := generateLiveSplitScriptData(sd)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := header.Execute(os.Stdout, tv); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
309
go/trains/darwin/darwindb/_schema.sql
Normal file
309
go/trains/darwin/darwindb/_schema.sql
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
DROP SCHEMA darwindb CASCADE;
|
||||||
|
CREATE SCHEMA darwindb;
|
||||||
|
SET search_path TO darwindb;
|
||||||
|
|
||||||
|
CREATE TABLE train_services (
|
||||||
|
id BIGSERIAL NOT NULL PRIMARY KEY,
|
||||||
|
rid VARCHAR(16) NOT NULL UNIQUE,
|
||||||
|
uid VARCHAR(6) NOT NULL,
|
||||||
|
rsid VARCHAR(8) NOT NULL,
|
||||||
|
headcode VARCHAR(4) NOT NULL,
|
||||||
|
scheduled_start_date DATE NOT NULL,
|
||||||
|
effective_start_time TIMESTAMPTZ NULL, /* computed for ease of use */
|
||||||
|
|
||||||
|
train_operator VARCHAR(2) NOT NULL,
|
||||||
|
status VARCHAR(2) NOT NULL DEFAULT 'P',
|
||||||
|
train_category VARCHAR(2) NOT NULL DEFAULT 'OO',
|
||||||
|
is_passenger_svc BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
is_charter BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
is_q_train BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
|
||||||
|
delay_reason_code VARCHAR NULL,
|
||||||
|
delay_reason_tiploc VARCHAR NULL,
|
||||||
|
delay_reason_tiploc_near BOOLEAN NULL,
|
||||||
|
|
||||||
|
cancellation_reason_code VARCHAR NULL,
|
||||||
|
cancellation_reason_tiploc VARCHAR NULL,
|
||||||
|
cancellation_reason_tiploc_near BOOLEAN NULL,
|
||||||
|
|
||||||
|
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
cancelled BOOLEAN NOT NULL DEFAULT FALSE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX train_services_by_ssd_uid ON train_services (scheduled_start_date, uid);
|
||||||
|
CREATE INDEX train_services_by_ssd_rsid ON train_services (scheduled_start_date, rsid);
|
||||||
|
CREATE INDEX train_services_by_ssd_headcode ON train_services (scheduled_start_date, headcode);
|
||||||
|
|
||||||
|
CREATE TYPE timestamp_type AS ENUM ('est', 'act');
|
||||||
|
CREATE TYPE platform_source AS ENUM ('planned', 'automatic', 'manual');
|
||||||
|
CREATE TYPE calling_point_type AS ENUM ('OR', 'OPOR', 'IP', 'OPIP', 'PP', 'DT', 'OPDT');
|
||||||
|
|
||||||
|
CREATE TABLE train_locations (
|
||||||
|
id BIGSERIAL NOT NULL PRIMARY KEY,
|
||||||
|
tsid BIGINT NOT NULL REFERENCES train_services (id) ON DELETE CASCADE,
|
||||||
|
rid VARCHAR(16) NOT NULL REFERENCES train_services (rid) ON DELETE CASCADE,
|
||||||
|
tiploc VARCHAR(7) NOT NULL,
|
||||||
|
|
||||||
|
-- From Darwin Schedule
|
||||||
|
calling_point calling_point_type NULL,
|
||||||
|
schedule_public_arrival TIMESTAMPTZ NULL,
|
||||||
|
schedule_working_arrival TIMESTAMPTZ NULL,
|
||||||
|
schedule_public_departure TIMESTAMPTZ NULL,
|
||||||
|
schedule_working_departure TIMESTAMPTZ NULL,
|
||||||
|
-- No concept of a "public pass"
|
||||||
|
schedule_working_pass TIMESTAMPTZ NULL,
|
||||||
|
schedule_route_delay SMALLINT NULL,
|
||||||
|
schedule_activity VARCHAR(12) NULL,
|
||||||
|
schedule_planned_activity VARCHAR(12) NULL,
|
||||||
|
schedule_cancelled BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
schedule_false_destination_tiploc VARCHAR(7) NULL,
|
||||||
|
schedule_platform VARCHAR(5) NULL,
|
||||||
|
|
||||||
|
-- Indicates whether this appeared in a schedule we've seen previously, but not in the latest version.
|
||||||
|
active_in_schedule BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
-- From Darwin TrainStatus
|
||||||
|
service_suppressed BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
platform VARCHAR(5) NULL,
|
||||||
|
platform_suppressed BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
cis_platform_suppression BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
platform_source platform_source NOT NULL DEFAULT 'planned',
|
||||||
|
platform_confirmed BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
train_length SMALLINT NULL,
|
||||||
|
|
||||||
|
-- estimated, working, actual
|
||||||
|
-- arrival, departure, pass
|
||||||
|
estimated_arrival TIMESTAMPTZ NULL,
|
||||||
|
working_estimated_arrival TIMESTAMPTZ NULL,
|
||||||
|
actual_arrival TIMESTAMPTZ NULL,
|
||||||
|
arrival_data JSONB NULL,
|
||||||
|
|
||||||
|
estimated_departure TIMESTAMPTZ NULL,
|
||||||
|
working_estimated_departure TIMESTAMPTZ NULL,
|
||||||
|
actual_departure TIMESTAMPTZ NULL,
|
||||||
|
departure_data JSONB NULL,
|
||||||
|
|
||||||
|
estimated_pass TIMESTAMPTZ NULL,
|
||||||
|
working_estimated_pass TIMESTAMPTZ NULL,
|
||||||
|
actual_pass TIMESTAMPTZ NULL,
|
||||||
|
pass_data JSONB NULL,
|
||||||
|
|
||||||
|
CONSTRAINT train_locations_tiploc_wtd UNIQUE (tsid, tiploc, schedule_working_departure),
|
||||||
|
CONSTRAINT train_locations_tiploc_ptd UNIQUE (tsid, tiploc, schedule_public_departure),
|
||||||
|
CONSTRAINT train_locations_tiploc_wta UNIQUE (tsid, tiploc, schedule_working_arrival),
|
||||||
|
CONSTRAINT train_locations_tiploc_pta UNIQUE (tsid, tiploc, schedule_public_arrival),
|
||||||
|
CONSTRAINT train_locations_tiploc_wtp UNIQUE (tsid, tiploc, schedule_working_pass)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX train_locations_by_tsid ON train_locations (tsid, tiploc);
|
||||||
|
CREATE INDEX train_locations_by_rid ON train_locations (rid, tiploc);
|
||||||
|
|
||||||
|
CREATE FUNCTION train_locations_upsert_from_schedule (
|
||||||
|
p_tsid BIGINT,
|
||||||
|
p_rid VARCHAR(16),
|
||||||
|
p_tiploc VARCHAR(7),
|
||||||
|
p_calling_point calling_point_type,
|
||||||
|
p_schedule_public_arrival TIMESTAMPTZ,
|
||||||
|
p_schedule_working_arrival TIMESTAMPTZ,
|
||||||
|
p_schedule_public_departure TIMESTAMPTZ,
|
||||||
|
p_schedule_working_departure TIMESTAMPTZ,
|
||||||
|
p_schedule_working_pass TIMESTAMPTZ,
|
||||||
|
p_schedule_route_delay SMALLINT,
|
||||||
|
p_schedule_activity VARCHAR(12),
|
||||||
|
p_schedule_planned_activity VARCHAR(12),
|
||||||
|
p_schedule_cancelled BOOLEAN,
|
||||||
|
p_schedule_false_destination_tiploc VARCHAR(7),
|
||||||
|
p_schedule_platform VARCHAR(3)) RETURNS BIGINT AS
|
||||||
|
$$
|
||||||
|
-- There is one problem with this function, which is why it's not generic and using ON CONFLICT DO UPDATE:
|
||||||
|
-- if we notice that one particular constraint has caused a problem, so we do an update, and _that_ update causes a problem
|
||||||
|
-- then we're stuck; in this instance we'll just explode.
|
||||||
|
--
|
||||||
|
-- Imagine the following (unlikely) case:
|
||||||
|
-- Old schedule: (tsid, tiploc, wtd, ptd):
|
||||||
|
-- (1, "AAAAAA", 01:00, 01:00)
|
||||||
|
-- (1, "AAAAAA", 01:10, 01:10)
|
||||||
|
-- If we then try to update with a new schedule location (1, "AAAAAA", 01:00, 01:10), we cause a conflict.
|
||||||
|
BEGIN
|
||||||
|
LOOP
|
||||||
|
DECLARE v_locid BIGINT;
|
||||||
|
DECLARE v_constraint_name TEXT;
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO train_locations
|
||||||
|
(tsid, rid, tiploc, calling_point,
|
||||||
|
schedule_public_arrival, schedule_working_arrival,
|
||||||
|
schedule_public_departure, schedule_working_departure,
|
||||||
|
schedule_working_pass,
|
||||||
|
schedule_route_delay, schedule_activity, schedule_planned_activity, schedule_cancelled,
|
||||||
|
schedule_false_destination_tiploc, schedule_platform)
|
||||||
|
VALUES
|
||||||
|
(p_tsid, p_rid, p_tiploc, p_calling_point,
|
||||||
|
p_schedule_public_arrival, p_schedule_working_arrival,
|
||||||
|
p_schedule_public_departure, p_schedule_working_departure,
|
||||||
|
p_schedule_working_pass,
|
||||||
|
p_schedule_route_delay, p_schedule_activity, p_schedule_planned_activity, p_schedule_cancelled,
|
||||||
|
p_schedule_false_destination_tiploc, p_schedule_platform)
|
||||||
|
RETURNING id INTO v_locid;
|
||||||
|
RETURN v_locid;
|
||||||
|
EXCEPTION WHEN unique_violation THEN
|
||||||
|
-- Work out which constraint caused this.
|
||||||
|
GET STACKED DIAGNOSTICS v_constraint_name = CONSTRAINT_NAME;
|
||||||
|
CASE v_constraint_name
|
||||||
|
WHEN 'train_locations_tiploc_wtd' THEN
|
||||||
|
UPDATE train_locations SET
|
||||||
|
calling_point=p_calling_point,
|
||||||
|
schedule_public_arrival=p_schedule_public_arrival, schedule_working_arrival=p_schedule_working_arrival,
|
||||||
|
schedule_public_departure=p_schedule_public_departure, /* wtd */
|
||||||
|
schedule_working_pass=p_schedule_working_pass,
|
||||||
|
schedule_route_delay=p_schedule_route_delay, schedule_activity=p_schedule_activity, schedule_planned_activity=p_schedule_planned_activity, schedule_cancelled=p_schedule_cancelled,
|
||||||
|
schedule_false_destination_tiploc=p_schedule_false_destination_tiploc, schedule_platform=p_schedule_platform,
|
||||||
|
active_in_schedule=true
|
||||||
|
WHERE
|
||||||
|
tsid=p_tsid AND tiploc=p_tiploc AND schedule_working_departure=p_schedule_working_departure
|
||||||
|
RETURNING id INTO v_locid;
|
||||||
|
IF found THEN RETURN v_locid; END IF;
|
||||||
|
-- We didn't find it? Try again.
|
||||||
|
WHEN 'train_locations_tiploc_ptd' THEN
|
||||||
|
UPDATE train_locations SET
|
||||||
|
calling_point=p_calling_point,
|
||||||
|
schedule_public_arrival=p_schedule_public_arrival, schedule_working_arrival=p_schedule_working_arrival,
|
||||||
|
/* ptd */ schedule_working_departure=p_schedule_working_departure,
|
||||||
|
schedule_working_pass=p_schedule_working_pass,
|
||||||
|
schedule_route_delay=p_schedule_route_delay, schedule_activity=p_schedule_activity, schedule_planned_activity=p_schedule_planned_activity, schedule_cancelled=p_schedule_cancelled,
|
||||||
|
schedule_false_destination_tiploc=p_schedule_false_destination_tiploc, schedule_platform=p_schedule_platform,
|
||||||
|
active_in_schedule=true
|
||||||
|
WHERE
|
||||||
|
tsid=p_tsid AND tiploc=p_tiploc AND schedule_public_departure=p_schedule_public_departure
|
||||||
|
RETURNING id INTO v_locid;
|
||||||
|
IF found THEN RETURN v_locid; END IF;
|
||||||
|
-- We didn't find it? Try again.
|
||||||
|
WHEN 'train_locations_tiploc_wta' THEN
|
||||||
|
UPDATE train_locations SET
|
||||||
|
calling_point=p_calling_point,
|
||||||
|
schedule_public_arrival=p_schedule_public_arrival, /* wta */
|
||||||
|
schedule_public_departure=p_schedule_public_departure, schedule_working_departure=p_schedule_working_departure,
|
||||||
|
schedule_working_pass=p_schedule_working_pass,
|
||||||
|
schedule_route_delay=p_schedule_route_delay, schedule_activity=p_schedule_activity, schedule_planned_activity=p_schedule_planned_activity, schedule_cancelled=p_schedule_cancelled,
|
||||||
|
schedule_false_destination_tiploc=p_schedule_false_destination_tiploc, schedule_platform=p_schedule_platform,
|
||||||
|
active_in_schedule=true
|
||||||
|
WHERE
|
||||||
|
tsid=p_tsid AND tiploc=p_tiploc AND schedule_working_arrival=p_schedule_working_arrival
|
||||||
|
RETURNING id INTO v_locid;
|
||||||
|
IF found THEN RETURN v_locid; END IF;
|
||||||
|
-- We didn't find it? Try again.
|
||||||
|
WHEN 'train_locations_tiploc_pta' THEN
|
||||||
|
UPDATE train_locations SET
|
||||||
|
calling_point=p_calling_point,
|
||||||
|
/* pta */ schedule_working_arrival=p_schedule_working_arrival,
|
||||||
|
schedule_public_departure=p_schedule_public_departure, schedule_working_departure=p_schedule_working_departure,
|
||||||
|
schedule_working_pass=p_schedule_working_pass,
|
||||||
|
schedule_route_delay=p_schedule_route_delay, schedule_activity=p_schedule_activity, schedule_planned_activity=p_schedule_planned_activity, schedule_cancelled=p_schedule_cancelled,
|
||||||
|
schedule_false_destination_tiploc=p_schedule_false_destination_tiploc, schedule_platform=p_schedule_platform,
|
||||||
|
active_in_schedule=true
|
||||||
|
WHERE
|
||||||
|
tsid=p_tsid AND tiploc=p_tiploc AND schedule_public_arrival=p_schedule_public_arrival
|
||||||
|
RETURNING id INTO v_locid;
|
||||||
|
IF found THEN RETURN v_locid; END IF;
|
||||||
|
-- We didn't find it? Try again.
|
||||||
|
WHEN 'train_locations_tiploc_wtp' THEN
|
||||||
|
UPDATE train_locations SET
|
||||||
|
calling_point=p_calling_point,
|
||||||
|
schedule_public_arrival=p_schedule_public_arrival, schedule_working_arrival=p_schedule_working_arrival,
|
||||||
|
schedule_public_departure=p_schedule_public_departure, schedule_working_departure=p_schedule_working_departure,
|
||||||
|
/* wtp */
|
||||||
|
schedule_route_delay=p_schedule_route_delay, schedule_activity=p_schedule_activity, schedule_planned_activity=p_schedule_planned_activity, schedule_cancelled=p_schedule_cancelled,
|
||||||
|
schedule_false_destination_tiploc=p_schedule_false_destination_tiploc, schedule_platform=p_schedule_platform,
|
||||||
|
active_in_schedule=true
|
||||||
|
WHERE
|
||||||
|
tsid=p_tsid AND tiploc=p_tiploc AND schedule_working_pass=p_schedule_working_pass
|
||||||
|
RETURNING id INTO v_locid;
|
||||||
|
IF found THEN RETURN v_locid; END IF;
|
||||||
|
-- We didn't find it? Try again.
|
||||||
|
WHEN 'train_locations_tiploc_wtd' THEN
|
||||||
|
UPDATE train_locations SET
|
||||||
|
calling_point=p_calling_point,
|
||||||
|
schedule_public_arrival=p_schedule_public_arrival, schedule_working_arrival=p_schedule_working_arrival,
|
||||||
|
schedule_public_departure=p_schedule_public_departure, schedule_working_departure=p_schedule_working_departure,
|
||||||
|
schedule_working_pass=p_schedule_working_pass,
|
||||||
|
schedule_route_delay=p_schedule_route_delay, schedule_activity=p_schedule_activity, schedule_planned_activity=p_schedule_planned_activity, schedule_cancelled=p_schedule_cancelled,
|
||||||
|
schedule_false_destination_tiploc=p_schedule_false_destination_tiploc, schedule_platform=p_schedule_platform,
|
||||||
|
active_in_schedule=true
|
||||||
|
WHERE
|
||||||
|
tsid=p_tsid AND tiploc=p_tiploc AND schedule_working_departure=p_schedule_working_departure
|
||||||
|
RETURNING id INTO v_locid;
|
||||||
|
IF found THEN RETURN v_locid; END IF;
|
||||||
|
-- We didn't find it? Try again.
|
||||||
|
ELSE
|
||||||
|
RAISE; -- don't know how to deal with this particular constraint.
|
||||||
|
END CASE;
|
||||||
|
END;
|
||||||
|
END LOOP;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE TABLE train_alerts (
|
||||||
|
id BIGSERIAL NOT NULL PRIMARY KEY,
|
||||||
|
alert_id BIGINT NOT NULL,
|
||||||
|
|
||||||
|
source VARCHAR(10) NOT NULL,
|
||||||
|
alert_text TEXT NOT NULL,
|
||||||
|
audience VARCHAR(15) NOT NULL,
|
||||||
|
alert_type VARCHAR(15) NOT NULL,
|
||||||
|
|
||||||
|
send_alert_by_sms BOOLEAN NOT NULL,
|
||||||
|
send_alert_by_email BOOLEAN NOT NULL,
|
||||||
|
send_alert_by_twitter BOOLEAN NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE train_alert_services (
|
||||||
|
id BIGSERIAL NOT NULL PRIMARY KEY,
|
||||||
|
ta_id BIGINT NOT NULL REFERENCES train_alerts (id) ON DELETE CASCADE,
|
||||||
|
service_id BIGINT NOT NULL REFERENCES train_services (id) ON DELETE CASCADE,
|
||||||
|
crs VARCHAR(3) NULL,
|
||||||
|
|
||||||
|
CONSTRAINT train_alert_services_by_service_id_crs UNIQUE (service_id, crs, ta_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE ref_tocs (
|
||||||
|
toc VARCHAR(2) PRIMARY KEY NOT NULL,
|
||||||
|
name VARCHAR(256) NOT NULL,
|
||||||
|
url VARCHAR(512) NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE ref_locations (
|
||||||
|
tiploc VARCHAR(7) PRIMARY KEY NOT NULL,
|
||||||
|
name VARCHAR(30) NULL,
|
||||||
|
override_name VARCHAR(30) NULL,
|
||||||
|
crs VARCHAR(3) NULL,
|
||||||
|
toc VARCHAR(2) NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX ref_locations_by_crs ON ref_locations (crs);
|
||||||
|
|
||||||
|
CREATE TABLE ref_cancel_reasons (
|
||||||
|
code INT NOT NULL PRIMARY KEY,
|
||||||
|
text VARCHAR(256) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE ref_late_running_reasons (
|
||||||
|
code INT NOT NULL PRIMARY KEY,
|
||||||
|
text VARCHAR(256) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE ref_via (
|
||||||
|
id BIGSERIAL NOT NULL PRIMARY KEY,
|
||||||
|
at_crs VARCHAR(3) NOT NULL,
|
||||||
|
dest_tiploc VARCHAR(7) NOT NULL,
|
||||||
|
loc1_tiploc VARCHAR(7) NOT NULL,
|
||||||
|
loc2_tiploc VARCHAR(7) NOT NULL DEFAULT '',
|
||||||
|
text VARCHAR(256) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT ref_via_by_at_dest_loc1_loc2 UNIQUE (at_crs, dest_tiploc, loc1_tiploc, loc2_tiploc)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE ref_cis (
|
||||||
|
code VARCHAR(4) NOT NULL PRIMARY KEY,
|
||||||
|
name VARCHAR(60) NOT NULL
|
||||||
|
);
|
65
go/trains/darwin/darwindb/affected.go
Normal file
65
go/trains/darwin/darwindb/affected.go
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
package darwindb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Affected tracks the TIPLOCs, RIDs and TSIDs which have been updated.
|
||||||
|
type Affected struct {
|
||||||
|
TIPLOCs map[string]bool
|
||||||
|
RIDs map[string]bool
|
||||||
|
TSIDs map[int]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAffected creates a new Affected instance.
|
||||||
|
func NewAffected() *Affected {
|
||||||
|
return &Affected{
|
||||||
|
TIPLOCs: make(map[string]bool),
|
||||||
|
RIDs: make(map[string]bool),
|
||||||
|
TSIDs: make(map[int]bool),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TIPLOC marks the given TIPLOC code as affected.
|
||||||
|
func (a *Affected) TIPLOC(t string) {
|
||||||
|
if a != nil {
|
||||||
|
a.TIPLOCs[t] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RID marks the given train RID identifier as affected.
|
||||||
|
func (a *Affected) RID(r string) {
|
||||||
|
if a != nil {
|
||||||
|
a.RIDs[r] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TSID marks the given train service ID (internal ID) as affected.
|
||||||
|
func (a *Affected) TSID(t int) {
|
||||||
|
if a != nil {
|
||||||
|
a.TSIDs[t] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Summary returns a string summary of affected assets.
|
||||||
|
func (a *Affected) Summary() string {
|
||||||
|
if a == nil {
|
||||||
|
return "change tracking disabled"
|
||||||
|
}
|
||||||
|
|
||||||
|
var s []string
|
||||||
|
if len(a.TIPLOCs) > 0 {
|
||||||
|
s = append(s, fmt.Sprintf("%d TIPLOCs", len(a.TIPLOCs)))
|
||||||
|
}
|
||||||
|
if len(a.RIDs) > 0 {
|
||||||
|
s = append(s, fmt.Sprintf("%d RIDs", len(a.RIDs)))
|
||||||
|
}
|
||||||
|
if len(a.TSIDs) > 0 {
|
||||||
|
s = append(s, fmt.Sprintf("%d TSIDs", len(a.TSIDs)))
|
||||||
|
}
|
||||||
|
if len(s) == 0 {
|
||||||
|
return "no changes"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%v", strings.Join(s, ", "))
|
||||||
|
}
|
71
go/trains/darwin/darwindb/darwindb.go
Normal file
71
go/trains/darwin/darwindb/darwindb.go
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
package darwindb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
pgx "github.com/jackc/pgx/v4"
|
||||||
|
"hg.lukegb.com/lukegb/depot/go/trains/darwin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// handleDataResponse handles a Darwin DataResponse message (i.e. a SnapshotResponse or a UpdateResponse).
|
||||||
|
func handleDataResponse(ctx context.Context, tx pgx.Tx, dr *darwin.DataResponse, a *Affected) error {
|
||||||
|
for _, s := range dr.Schedule {
|
||||||
|
if err := handleSchedule(ctx, tx, &s, a, scheduleModeOverwrite); err != nil {
|
||||||
|
return fmt.Errorf("handling schedule for RID %v: %w", s.RID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, ts := range dr.TrainStatus {
|
||||||
|
if err := handleTrainStatus(ctx, tx, &ts, a); err != nil {
|
||||||
|
return fmt.Errorf("handling trainstatus for RID %v: %w", ts.RID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, ds := range dr.Deactivated {
|
||||||
|
var tid int
|
||||||
|
row := tx.QueryRow(ctx, "UPDATE train_services SET active=FALSE WHERE rid=$1 RETURNING id", ds.RID)
|
||||||
|
if err := row.Scan(&tid); err == pgx.ErrNoRows {
|
||||||
|
log.Printf("deactivated schedule for unknown RID %v", ds.RID)
|
||||||
|
} else if err != nil {
|
||||||
|
return fmt.Errorf("handling deactivateschedule for RID %v: %w", ds.RID, err)
|
||||||
|
}
|
||||||
|
a.RID(ds.RID)
|
||||||
|
a.TSID(tid)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessTimetable updates the database by processing a PportTimetable message in the given transaction.
|
||||||
|
func ProcessTimetable(ctx context.Context, tx pgx.Tx, ppt *darwin.PushPortTimetable, a *Affected) error {
|
||||||
|
for _, s := range ppt.Journey {
|
||||||
|
if err := handleSchedule(ctx, tx, &s, a, scheduleModeIgnoreExisting); err != nil {
|
||||||
|
return fmt.Errorf("handling timetable journey for RID %v: %w", s.RID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process updates the database by processing a single PushPort message in the given transaction.
|
||||||
|
func Process(ctx context.Context, tx pgx.Tx, pp *darwin.PushPort, a *Affected) error {
|
||||||
|
drs := make([]*darwin.DataResponse, 0, len(pp.SnapshotResp)+len(pp.UpdateResp))
|
||||||
|
for _, r := range pp.SnapshotResp {
|
||||||
|
drs = append(drs, &r.DataResponse)
|
||||||
|
}
|
||||||
|
for _, r := range pp.UpdateResp {
|
||||||
|
drs = append(drs, &r.DataResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(drs) == 0 {
|
||||||
|
// Short-circuit if we don't have anything we're interested in - which is very unlikely.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dr := range drs {
|
||||||
|
if err := handleDataResponse(ctx, tx, dr, a); err != nil {
|
||||||
|
return fmt.Errorf("handling data response: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
103
go/trains/darwin/darwindb/ddbref.go
Normal file
103
go/trains/darwin/darwindb/ddbref.go
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
package darwindb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
pgx "github.com/jackc/pgx/v4"
|
||||||
|
"hg.lukegb.com/lukegb/depot/go/trains/darwin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessReferenceData updates the database by processing a PportReferenceData message in the given transaction.
|
||||||
|
func ProcessReferenceData(ctx context.Context, tx pgx.Tx, pprd *darwin.PushPortReferenceData, a *Affected) error {
|
||||||
|
crsToTIPLOC := make(map[string]string)
|
||||||
|
for _, loc := range pprd.Locations {
|
||||||
|
if loc.CRS != nil {
|
||||||
|
crsToTIPLOC[*loc.CRS] = loc.TIPLOC
|
||||||
|
}
|
||||||
|
_, err := tx.Exec(ctx, `
|
||||||
|
INSERT INTO ref_locations
|
||||||
|
(tiploc, name, crs, toc)
|
||||||
|
VALUES
|
||||||
|
($1, $2, $3, $4)
|
||||||
|
ON CONFLICT (tiploc) DO UPDATE
|
||||||
|
SET name=COALESCE(ref_locations.override_name, $2), crs=$3, toc=$4
|
||||||
|
`, loc.TIPLOC, loc.Name, loc.CRS, loc.TOC)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("updating location %q: %w", loc.TIPLOC, err)
|
||||||
|
}
|
||||||
|
a.TIPLOC(loc.TIPLOC)
|
||||||
|
}
|
||||||
|
for _, toc := range pprd.TOCs {
|
||||||
|
_, err := tx.Exec(ctx, `
|
||||||
|
INSERT INTO ref_tocs
|
||||||
|
(toc, name, url)
|
||||||
|
VALUES
|
||||||
|
($1, $2, $3)
|
||||||
|
ON CONFLICT (toc) DO UPDATE
|
||||||
|
SET name=$2, url=$3
|
||||||
|
`, toc.TOC, toc.Name, toc.URL)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("updating TOC %q: %w", toc.TOC, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, r := range pprd.LateRunningReasons {
|
||||||
|
_, err := tx.Exec(ctx, `
|
||||||
|
INSERT INTO ref_late_running_reasons
|
||||||
|
(code, text)
|
||||||
|
VALUES
|
||||||
|
($1, $2)
|
||||||
|
ON CONFLICT (code) DO UPDATE
|
||||||
|
SET text=$2
|
||||||
|
`, r.Code, r.Text)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("updating LateRunningReason %q: %w", r.Code, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, r := range pprd.CancellationReasons {
|
||||||
|
_, err := tx.Exec(ctx, `
|
||||||
|
INSERT INTO ref_cancel_reasons
|
||||||
|
(code, text)
|
||||||
|
VALUES
|
||||||
|
($1, $2)
|
||||||
|
ON CONFLICT (code) DO UPDATE
|
||||||
|
SET text=$2
|
||||||
|
`, r.Code, r.Text)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("updating CancellationReason %q: %w", r.Code, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, v := range pprd.Via {
|
||||||
|
_, err := tx.Exec(ctx, `
|
||||||
|
INSERT INTO ref_via
|
||||||
|
(at_crs, dest_tiploc, loc1_tiploc, loc2_tiploc, text)
|
||||||
|
VALUES
|
||||||
|
($1, $2, $3, $4, $5)
|
||||||
|
ON CONFLICT (at_crs, dest_tiploc, loc1_tiploc, loc2_tiploc) DO UPDATE
|
||||||
|
SET text=$5
|
||||||
|
RETURNING at_crs
|
||||||
|
`, v.AtCRS, v.DestTIPLOC, v.Loc1TIPLOC, v.Loc2TIPLOC, v.Text)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("updating Via %v/%v/%v/%v: %w", v.AtCRS, v.DestTIPLOC, v.Loc1TIPLOC, v.Loc2TIPLOC, err)
|
||||||
|
}
|
||||||
|
tiploc, ok := crsToTIPLOC[v.AtCRS]
|
||||||
|
if ok {
|
||||||
|
a.TIPLOC(tiploc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, cis := range pprd.CISSource {
|
||||||
|
_, err := tx.Exec(ctx, `
|
||||||
|
INSERT INTO ref_cis
|
||||||
|
(code, name)
|
||||||
|
VALUES
|
||||||
|
($1, $2)
|
||||||
|
ON CONFLICT (code) DO UPDATE
|
||||||
|
SET name=$2
|
||||||
|
`, cis.CISCode, cis.Name)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("updating CISSource %v: %w", cis.CISCode, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
112
go/trains/darwin/darwindb/ddbschedule.go
Normal file
112
go/trains/darwin/darwindb/ddbschedule.go
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
package darwindb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
pgx "github.com/jackc/pgx/v4"
|
||||||
|
"hg.lukegb.com/lukegb/depot/go/trains/darwin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type scheduleMode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
scheduleModeOverwrite scheduleMode = iota
|
||||||
|
scheduleModeIgnoreExisting
|
||||||
|
)
|
||||||
|
|
||||||
|
// handleSchedule processes a Darwin Schedule.
|
||||||
|
func handleSchedule(ctx context.Context, tx pgx.Tx, s *darwin.Schedule, a *Affected, sm scheduleMode) error {
|
||||||
|
startTS, err := startTime(s)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("finding start time for RID %v: %w", s.RID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cancelCode, cancelTIPLOC *string
|
||||||
|
var cancelTIPLOCNear *bool
|
||||||
|
if s.CancellationReason != nil {
|
||||||
|
cancelCode = &s.CancellationReason.Code
|
||||||
|
cancelTIPLOC = &s.CancellationReason.TIPLOC
|
||||||
|
cancelTIPLOCNear = &s.CancellationReason.Near
|
||||||
|
}
|
||||||
|
sql := `
|
||||||
|
INSERT INTO train_services
|
||||||
|
(rid, uid, rsid, headcode, scheduled_start_date, effective_start_time,
|
||||||
|
train_operator, status, train_category, is_passenger_svc, is_charter, is_q_train,
|
||||||
|
cancellation_reason_code, cancellation_reason_tiploc, cancellation_reason_tiploc_near,
|
||||||
|
active, deleted, cancelled)
|
||||||
|
VALUES
|
||||||
|
($1, $2, $3, $4, $5, $6,
|
||||||
|
$7, COALESCE($8, 'P'), COALESCE($9, 'OO'), COALESCE($10, true), COALESCE($11, false), COALESCE($12, false),
|
||||||
|
$13, $14, $15,
|
||||||
|
COALESCE($16, true), $17, COALESCE($18, false))
|
||||||
|
ON CONFLICT (rid)`
|
||||||
|
switch sm {
|
||||||
|
case scheduleModeOverwrite:
|
||||||
|
sql += ` DO UPDATE SET
|
||||||
|
uid=$2, rsid=$3, headcode=$4, scheduled_start_date=$5, effective_start_time=$6,
|
||||||
|
train_operator=$7, status=COALESCE($8, 'P'), train_category=COALESCE($9, 'OO'), is_passenger_svc=COALESCE($10, true), is_charter=COALESCE($11, false), is_q_train=COALESCE($12, train_services.is_q_train),
|
||||||
|
cancellation_reason_code=$13, cancellation_reason_tiploc=$14, cancellation_reason_tiploc_near=$15,
|
||||||
|
active=COALESCE($16, 'true'), deleted=$17, cancelled=COALESCE($18, 'false')
|
||||||
|
`
|
||||||
|
case scheduleModeIgnoreExisting:
|
||||||
|
sql += ` DO NOTHING
|
||||||
|
`
|
||||||
|
}
|
||||||
|
sql += `RETURNING id`
|
||||||
|
row := tx.QueryRow(ctx, sql,
|
||||||
|
s.RID, s.UID, s.RSID, s.TrainID, s.SSD, startTS,
|
||||||
|
s.TOC, s.Status, s.TrainCat, s.IsPassengerSvc, s.IsCharter, s.IsQTrain,
|
||||||
|
cancelCode, cancelTIPLOC, cancelTIPLOCNear,
|
||||||
|
s.IsActive, s.Deleted, s.Cancelled)
|
||||||
|
var serviceID int
|
||||||
|
if err := row.Scan(&serviceID); err == pgx.ErrNoRows && sm == scheduleModeIgnoreExisting {
|
||||||
|
// The row existed already.
|
||||||
|
return nil
|
||||||
|
} else if err != nil {
|
||||||
|
log.Printf("inserting schedule for RID %v: %v", s.RID, err)
|
||||||
|
return fmt.Errorf("inserting schedule for RID %v: %w", s.RID, err)
|
||||||
|
}
|
||||||
|
a.RID(s.RID)
|
||||||
|
a.TSID(serviceID)
|
||||||
|
|
||||||
|
// Insert/update all the locations.
|
||||||
|
var activeLocations []int
|
||||||
|
for _, cp := range s.CallingPoints {
|
||||||
|
var locid int
|
||||||
|
row := tx.QueryRow(ctx, `
|
||||||
|
SELECT train_locations_upsert_from_schedule(
|
||||||
|
p_tsid => $1, p_rid => $2, p_tiploc => $3, p_calling_point => $4,
|
||||||
|
p_schedule_public_arrival => $5, p_schedule_working_arrival => $6,
|
||||||
|
p_schedule_public_departure => $7, p_schedule_working_departure => $8,
|
||||||
|
p_schedule_working_pass => $9,
|
||||||
|
p_schedule_route_delay => $10, p_schedule_activity => $11, p_schedule_planned_activity => $12, p_schedule_cancelled => $13,
|
||||||
|
p_schedule_false_destination_tiploc => $14, p_schedule_platform => $15)
|
||||||
|
`,
|
||||||
|
serviceID, s.RID, cp.TIPLOC, cp.XMLName.Local,
|
||||||
|
timeToTimestamp(cp.PTA, startTS), timeToTimestamp(cp.WTA, startTS),
|
||||||
|
timeToTimestamp(cp.PTD, startTS), timeToTimestamp(cp.WTD, startTS),
|
||||||
|
timeToTimestamp(cp.WTP, startTS),
|
||||||
|
cp.RDelay, cp.Act, cp.PlanAct, cp.Cancelled,
|
||||||
|
cp.FD, cp.Platform,
|
||||||
|
)
|
||||||
|
if err := row.Scan(&locid); err != nil {
|
||||||
|
log.Printf("updating location %v from RID %v/my ID %v: %v", cp.TIPLOC, s.RID, serviceID, err)
|
||||||
|
zz, _ := json.MarshalIndent(s, "", " ")
|
||||||
|
log.Println(string(zz))
|
||||||
|
zz, _ = json.MarshalIndent(cp, "", " ")
|
||||||
|
log.Println(string(zz))
|
||||||
|
return fmt.Errorf("updating location %v from RID %v/my ID %v: %w", cp.TIPLOC, s.RID, serviceID, err)
|
||||||
|
} else {
|
||||||
|
activeLocations = append(activeLocations, locid)
|
||||||
|
}
|
||||||
|
a.TIPLOC(cp.TIPLOC)
|
||||||
|
}
|
||||||
|
if _, err := tx.Exec(ctx, "UPDATE train_locations SET active_in_schedule=false WHERE tsid=$1 AND NOT (id = ANY ($2))", serviceID, activeLocations); err != nil {
|
||||||
|
return fmt.Errorf("marking old locations as removed from schedule for RID %v/my ID %v (active locations: %v): %w", s.RID, serviceID, activeLocations, err)
|
||||||
|
}
|
||||||
|
fmt.Printf("s")
|
||||||
|
return nil
|
||||||
|
}
|
130
go/trains/darwin/darwindb/ddbtrainstatus.go
Normal file
130
go/trains/darwin/darwindb/ddbtrainstatus.go
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
package darwindb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
pgx "github.com/jackc/pgx/v4"
|
||||||
|
"hg.lukegb.com/lukegb/depot/go/trains/darwin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// handleTrainStatus handles a Darwin "TS" (train status) message.
|
||||||
|
func handleTrainStatus(ctx context.Context, tx pgx.Tx, ts *darwin.TrainStatus, a *Affected) error {
|
||||||
|
var tsid int
|
||||||
|
var tsEST time.Time
|
||||||
|
if ts.LateReason != nil {
|
||||||
|
row := tx.QueryRow(ctx, `
|
||||||
|
UPDATE train_services
|
||||||
|
SET delay_reason_code=$2, delay_reason_tiploc=$3, delay_reason_tiploc_near=$4
|
||||||
|
WHERE rid=$1
|
||||||
|
RETURNING id, effective_start_time
|
||||||
|
`, ts.RID, ts.LateReason.Code, ts.LateReason.TIPLOC, ts.LateReason.Near)
|
||||||
|
if err := row.Scan(&tsid, &tsEST); err == pgx.ErrNoRows {
|
||||||
|
log.Printf("got TrainStatus (delayed) for RID %v which isn't known", ts.RID)
|
||||||
|
return nil
|
||||||
|
} else if err != nil {
|
||||||
|
return fmt.Errorf("updating delay reason: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
row := tx.QueryRow(ctx, "SELECT id, effective_start_time FROM train_services WHERE rid=$1", ts.RID)
|
||||||
|
if err := row.Scan(&tsid, &tsEST); err == pgx.ErrNoRows {
|
||||||
|
log.Printf("got TrainStatus for RID %v which isn't known", ts.RID)
|
||||||
|
return nil
|
||||||
|
} else if err != nil {
|
||||||
|
return fmt.Errorf("fetching train_services ID: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a.RID(ts.RID)
|
||||||
|
a.TSID(tsid)
|
||||||
|
|
||||||
|
for _, l := range ts.Location {
|
||||||
|
// OK, this is a bit hairy. We only get updates for things which have _changed_.
|
||||||
|
var els []interface{}
|
||||||
|
addEl := func(x interface{}) int {
|
||||||
|
els = append(els, x)
|
||||||
|
return len(els)
|
||||||
|
}
|
||||||
|
addSet := func(s string, x interface{}) string {
|
||||||
|
return fmt.Sprintf("%s=$%d", s, addEl(x))
|
||||||
|
}
|
||||||
|
addTSTimeData := func(prefix string, td *darwin.TSTimeData) []string {
|
||||||
|
if td == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
x := []interface{}{
|
||||||
|
"estimated_%s", timeToTimestamp(td.ET, tsEST),
|
||||||
|
"working_estimated_%s", timeToTimestamp(td.WET, tsEST),
|
||||||
|
"actual_%s", timeToTimestamp(td.AT, tsEST),
|
||||||
|
"%s_data", td,
|
||||||
|
}
|
||||||
|
out := make([]string, len(x)/2)
|
||||||
|
for n := 0; n < len(out); n++ {
|
||||||
|
out[n] = fmt.Sprintf(x[n*2].(string)+"=$%d", prefix, addEl(x[n*2+1]))
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
var setClause []string
|
||||||
|
setClause = append(setClause, addTSTimeData("arrival", l.Arr)...)
|
||||||
|
setClause = append(setClause, addTSTimeData("departure", l.Dep)...)
|
||||||
|
setClause = append(setClause, addTSTimeData("pass", l.Pass)...)
|
||||||
|
if l.Plat != nil {
|
||||||
|
setClause = append(setClause, addSet("platform", l.Plat.Platform))
|
||||||
|
setClause = append(setClause, addSet("platform_suppressed", l.Plat.PlatSup))
|
||||||
|
setClause = append(setClause, addSet("cis_platform_suppression", l.Plat.CisPlatSup))
|
||||||
|
platsrc := map[string]string{
|
||||||
|
"P": "planned",
|
||||||
|
"A": "automatic",
|
||||||
|
"M": "manual",
|
||||||
|
"": "planned",
|
||||||
|
}[l.Plat.PlatSrc]
|
||||||
|
setClause = append(setClause, addSet("platform_source", platsrc))
|
||||||
|
setClause = append(setClause, addSet("platform_confirmed", l.Plat.Conf))
|
||||||
|
}
|
||||||
|
if l.Suppr != nil {
|
||||||
|
setClause = append(setClause, addSet("service_suppressed", *l.Suppr))
|
||||||
|
}
|
||||||
|
if l.Length != nil {
|
||||||
|
setClause = append(setClause, addSet("train_length", *l.Length))
|
||||||
|
}
|
||||||
|
if len(setClause) == 0 {
|
||||||
|
// Nothing to set for this location.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var whereClause []string
|
||||||
|
for columnName, tsValue := range map[string]string{
|
||||||
|
"schedule_public_arrival": l.PTA,
|
||||||
|
"schedule_working_arrival": l.WTA,
|
||||||
|
"schedule_public_departure": l.PTD,
|
||||||
|
"schedule_working_departure": l.WTD,
|
||||||
|
"schedule_working_pass": l.WTP,
|
||||||
|
} {
|
||||||
|
if tsValue == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
whereClause = append(whereClause, addSet(columnName, timeToTimestamp(tsValue, tsEST)))
|
||||||
|
}
|
||||||
|
if len(whereClause) == 0 {
|
||||||
|
return fmt.Errorf("RID %v/my ID %v at TIPLOC %v: ended up with no CircularTimes-based WHERE", ts.RID, tsid, l.TIPLOC)
|
||||||
|
}
|
||||||
|
whereClause = append(whereClause, addSet("tiploc", l.TIPLOC))
|
||||||
|
whereClause = append(whereClause, addSet("tsid", tsid))
|
||||||
|
|
||||||
|
query := fmt.Sprintf("UPDATE train_locations SET %s WHERE active_in_schedule AND %s", strings.Join(setClause, ", "), strings.Join(whereClause, " AND "))
|
||||||
|
cmd, err := tx.Exec(ctx, query, els...)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("RID %v/my ID %v at TIPLOC %v: failed to update with query %q: %v", ts.RID, tsid, l.TIPLOC, query, err)
|
||||||
|
return fmt.Errorf("RID %v/my ID %v at TIPLOC %v: failed to update with query %q: %w", ts.RID, tsid, l.TIPLOC, query, err)
|
||||||
|
}
|
||||||
|
a.TIPLOC(l.TIPLOC)
|
||||||
|
if cmd.RowsAffected() != 1 {
|
||||||
|
return fmt.Errorf("RID %v/my ID %v at TIPLOC %v: query %q: wanted to update 1 row, updated %d", ts.RID, tsid, l.TIPLOC, query, cmd.RowsAffected())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf("t")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
21
go/trains/darwin/darwindb/default.nix
Normal file
21
go/trains/darwin/darwindb/default.nix
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }@args:
|
||||||
|
depot.third_party.buildGo.package {
|
||||||
|
name = "darwindb";
|
||||||
|
srcs = [
|
||||||
|
./affected.go
|
||||||
|
./darwindb.go
|
||||||
|
./ddbref.go
|
||||||
|
./ddbschedule.go
|
||||||
|
./ddbtrainstatus.go
|
||||||
|
./tsutil.go
|
||||||
|
];
|
||||||
|
path = "hg.lukegb.com/lukegb/depot/go/trains/darwin/darwindb";
|
||||||
|
deps = with depot.third_party; [
|
||||||
|
gopkgs."github.com".jackc.pgx.v4
|
||||||
|
depot.go.trains.darwin
|
||||||
|
];
|
||||||
|
}
|
74
go/trains/darwin/darwindb/tsutil.go
Normal file
74
go/trains/darwin/darwindb/tsutil.go
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package darwindb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"hg.lukegb.com/lukegb/depot/go/trains/darwin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// secondsify adds :00 onto the end of a string if it's HH:MM formatted, otherwise does nothing.
|
||||||
|
func secondsify(ts string) string {
|
||||||
|
if len(ts) == 5 { // xx:xx (no seconds)
|
||||||
|
return ts + ":00"
|
||||||
|
}
|
||||||
|
return ts
|
||||||
|
}
|
||||||
|
|
||||||
|
// startTime takes a train and ascertains its "start time".
|
||||||
|
func startTime(s *darwin.Schedule) (time.Time, error) {
|
||||||
|
// We assume these are sensibly sorted by Darwin.
|
||||||
|
if len(s.CallingPoints) == 0 {
|
||||||
|
return time.Time{}, fmt.Errorf("no calling points")
|
||||||
|
}
|
||||||
|
cp := s.CallingPoints[0]
|
||||||
|
|
||||||
|
var ts string
|
||||||
|
tsCandidates := []string{cp.WTD, cp.PTD, cp.WTP, cp.WTA, cp.PTA}
|
||||||
|
for _, tsc := range tsCandidates {
|
||||||
|
if tsc != "" {
|
||||||
|
ts = tsc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ts == "" {
|
||||||
|
return time.Time{}, fmt.Errorf("failed to find a suitable timestamp in first calling point")
|
||||||
|
}
|
||||||
|
|
||||||
|
ts = fmt.Sprintf("%v %v", s.SSD, secondsify(ts))
|
||||||
|
|
||||||
|
t, err := time.ParseInLocation("2006-01-02 15:04:05", ts, darwin.London)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, fmt.Errorf("parsing start time %q: %w", ts, err)
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// timeToTimestamp takes a string HH:MM and places it appropriately given the approximate 'start time' of the train.
|
||||||
|
func timeToTimestamp(ts string, startTime time.Time) *time.Time {
|
||||||
|
if ts == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ts = secondsify(ts)
|
||||||
|
|
||||||
|
t, err := time.ParseInLocation("2006-01-02 15:04:05", startTime.Format("2006-01-02 ")+ts, darwin.London)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Follow Darwin rules for determining time skips
|
||||||
|
// https://wiki.openraildata.com/index.php/Darwin:Schedule_Element#Ordering
|
||||||
|
difference := t.Sub(startTime)
|
||||||
|
switch {
|
||||||
|
case difference > -6*time.Hour && difference < 18*time.Hour:
|
||||||
|
return &t
|
||||||
|
case difference <= -6*time.Hour:
|
||||||
|
// e.g. 08:00 start time, 01:00 time "now" (-7 hrs) -- add a day
|
||||||
|
t = t.AddDate(0, 0, 1)
|
||||||
|
return &t
|
||||||
|
case difference >= 18*time.Hour:
|
||||||
|
// e.g. 01:00 start time, 22:00 time "now" (+18 hrs) -- sub a day
|
||||||
|
t = t.AddDate(0, 0, -1)
|
||||||
|
return &t
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("unhandled difference %v", difference))
|
||||||
|
}
|
310
go/trains/darwin/darwingest/darwingest.go
Normal file
310
go/trains/darwin/darwingest/darwingest.go
Normal file
|
@ -0,0 +1,310 @@
|
||||||
|
package darwingest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"context"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/cenkalti/backoff/v4"
|
||||||
|
"github.com/go-stomp/stomp/v3"
|
||||||
|
"github.com/jlaffaye/ftp"
|
||||||
|
"gocloud.dev/blob"
|
||||||
|
"hg.lukegb.com/lukegb/depot/go/trains/darwin"
|
||||||
|
"hg.lukegb.com/lukegb/depot/go/trains/darwin/darwingest/darwingestftp"
|
||||||
|
"hg.lukegb.com/lukegb/depot/go/trains/darwin/darwingest/darwingests3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func degzip(b []byte) ([]byte, error) {
|
||||||
|
gzr, err := gzip.NewReader(bytes.NewBuffer(b))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("gzip.NewReader: %w", err)
|
||||||
|
}
|
||||||
|
d, err := io.ReadAll(gzr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("io.ReadAll: %w", err)
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalGzipPushPort(m []byte) (*darwin.PushPort, error) {
|
||||||
|
d, err := degzip(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("degzip: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var pp darwin.PushPort
|
||||||
|
if err := xml.Unmarshal(d, &pp); err != nil {
|
||||||
|
return nil, fmt.Errorf("xml.Unmarshal(%q): %w", string(d), err)
|
||||||
|
}
|
||||||
|
return &pp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ridToDate(rid string) (time.Time, error) {
|
||||||
|
t, err := time.ParseInLocation("20060102", rid[0:8], darwin.London)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
if t.Hour() < 2 || (t.Hour() == 2 && t.Minute() < 45) {
|
||||||
|
t = t.AddDate(0, 0, -1)
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Verbose bool
|
||||||
|
|
||||||
|
DialStomp func(ctx context.Context) (*stomp.Conn, error)
|
||||||
|
StompSubscribe func(ctx context.Context, sc *stomp.Conn) (*stomp.Subscription, error)
|
||||||
|
DialFTP func(ctx context.Context) (*ftp.ServerConn, error)
|
||||||
|
Message func(pp *darwin.PushPort) error
|
||||||
|
MessageCatchUp func(pp *darwin.PushPort) error
|
||||||
|
Timetable func(ppt *darwin.PushPortTimetable) error
|
||||||
|
ReferenceData func(pprd *darwin.PushPortReferenceData) error
|
||||||
|
CatchUpComplete func(ctx context.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func Start(ctx context.Context, bucket *blob.Bucket, cfg Config) error {
|
||||||
|
return run(ctx, bucket, cfg, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CatchUpAndStart(ctx context.Context, bucket *blob.Bucket, cfg Config) error {
|
||||||
|
return run(ctx, bucket, cfg, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func run(ctx context.Context, bucket *blob.Bucket, cfg Config, runCatchup bool) error {
|
||||||
|
handleMsg := func(cb func(*darwin.PushPort) error) func(*darwin.PushPort) error {
|
||||||
|
return func(pp *darwin.PushPort) error {
|
||||||
|
for _, ttid := range pp.TimeTableID {
|
||||||
|
fn := strings.TrimSpace(ttid.Filename)
|
||||||
|
if fn != "" {
|
||||||
|
log.Printf("informed that there's a new timetable at %q", fn)
|
||||||
|
ppt, err := darwingests3.LoadTimetable(ctx, bucket, fn)
|
||||||
|
if err == darwingests3.ErrBadVersion {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("LoadTimetable(%q): %w", fn, err)
|
||||||
|
}
|
||||||
|
if err := cfg.Timetable(ppt); err != nil {
|
||||||
|
return fmt.Errorf("Timetable(%q): %w", fn, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn = strings.TrimSpace(ttid.ReferenceFilename)
|
||||||
|
if fn != "" {
|
||||||
|
log.Printf("informed that there's a new refsheet at %q", fn)
|
||||||
|
rd, err := darwingests3.LoadReferenceData(ctx, bucket, fn)
|
||||||
|
if err == darwingests3.ErrBadVersion {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("LoadReferenceData(%q): %w", fn, err)
|
||||||
|
}
|
||||||
|
if err := cfg.ReferenceData(rd); err != nil {
|
||||||
|
return fmt.Errorf("cfg.ReferenceData(%q): %w", fn, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cb(pp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if runCatchup {
|
||||||
|
// Load the latest reference data.
|
||||||
|
rd, err := darwingests3.LoadLatestReferenceData(ctx, bucket)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("LoadLatestReferenceData: %w", err)
|
||||||
|
}
|
||||||
|
if err := cfg.ReferenceData(rd); err != nil {
|
||||||
|
return fmt.Errorf("cfg.ReferenceData: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stompBackoff := backoff.NewExponentialBackOff()
|
||||||
|
stompBackoff.MaxElapsedTime = 24 * time.Hour
|
||||||
|
|
||||||
|
stompConn, err := cfg.DialStomp(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("DialStomp: %w", err)
|
||||||
|
}
|
||||||
|
defer stompConn.Disconnect()
|
||||||
|
|
||||||
|
sub, err := cfg.StompSubscribe(ctx, stompConn)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("StompSubscribe: %w", err)
|
||||||
|
}
|
||||||
|
defer sub.Unsubscribe()
|
||||||
|
|
||||||
|
drainConnection := func() {
|
||||||
|
if sub == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go sub.Unsubscribe()
|
||||||
|
for m := range sub.C {
|
||||||
|
stompConn.Nack(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if runCatchup {
|
||||||
|
if err := func() error {
|
||||||
|
log.Println("awaiting first message to find replay timestamp")
|
||||||
|
|
||||||
|
// Find the start timestamp we need to replay up to.
|
||||||
|
m := <-sub.C
|
||||||
|
defer stompConn.Nack(m)
|
||||||
|
d, err := unmarshalGzipPushPort(m.Body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unmarshalGzipPushPort: %w", err)
|
||||||
|
}
|
||||||
|
log.Printf("first message is at %v", d.Time)
|
||||||
|
// Don't handle the message just yet.
|
||||||
|
|
||||||
|
rids := d.AffectedRIDs()
|
||||||
|
var timetableDate time.Time
|
||||||
|
if len(rids) == 0 {
|
||||||
|
log.Printf("no affected RIDs in first message! falling back to time.Now...")
|
||||||
|
timetableDate = time.Now()
|
||||||
|
} else {
|
||||||
|
timetableDate, err = ridToDate(rids[0])
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("couldn't parse RID %q to date, falling back to time.Now: %v", rids[0], err)
|
||||||
|
timetableDate = time.Now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the timetable first.
|
||||||
|
log.Println("loading initial timetable")
|
||||||
|
ppt, err := darwingests3.LoadTimetableForDate(ctx, bucket, timetableDate)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("LoadTimetableForDate: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("feeding timetable to database")
|
||||||
|
if err := cfg.Timetable(ppt); err != nil {
|
||||||
|
return fmt.Errorf("Timetable: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("connecting to FTP")
|
||||||
|
sc, err := cfg.DialFTP(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("DialFTP: %w", err)
|
||||||
|
}
|
||||||
|
defer sc.Quit()
|
||||||
|
|
||||||
|
// Grab the full snapshot dump
|
||||||
|
log.Println("loading snapshot")
|
||||||
|
lw, _, err := darwingestftp.LoadSnapshot(ctx, sc, handleMsg(cfg.MessageCatchUp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("LoadSnapshot: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now try to "catch it up" using the push-port logs to the start timestamp of the message...
|
||||||
|
log.Println("loading logs")
|
||||||
|
if err := darwingestftp.LoadLogs(ctx, sc, lw, d.Time, handleMsg(cfg.MessageCatchUp)); err != nil {
|
||||||
|
return fmt.Errorf("LoadLogs: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("marking catchup complete")
|
||||||
|
if err := cfg.CatchUpComplete(ctx); err != nil {
|
||||||
|
return fmt.Errorf("CatchUpComplete callback: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("we think we've caught up! let's go...")
|
||||||
|
if err := handleMsg(cfg.Message)(d); err != nil {
|
||||||
|
return fmt.Errorf("replaying first pushport message: %w", err)
|
||||||
|
}
|
||||||
|
if err := stompConn.Ack(m); err != nil {
|
||||||
|
return fmt.Errorf("acking first pushport message: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
drainConnection()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := cfg.CatchUpComplete(ctx); err != nil {
|
||||||
|
return fmt.Errorf("CatchUpComplete callback: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mustReconnect := false
|
||||||
|
for {
|
||||||
|
if sub == nil {
|
||||||
|
mustReconnect = true
|
||||||
|
}
|
||||||
|
if mustReconnect {
|
||||||
|
duration := stompBackoff.NextBackOff()
|
||||||
|
if duration == backoff.Stop {
|
||||||
|
drainConnection()
|
||||||
|
return fmt.Errorf("exceeded stomp reconnection retries")
|
||||||
|
}
|
||||||
|
log.Printf("subscription appears to have closed, retrying after %v...", duration)
|
||||||
|
if err := darwin.Sleep(ctx, duration); err != nil {
|
||||||
|
drainConnection()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
log.Printf("resubscribing...")
|
||||||
|
sub, err = cfg.StompSubscribe(ctx, stompConn)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("StompSubscribe while resubscribing: %v", err)
|
||||||
|
// Force close the server connection and dial again.
|
||||||
|
stompConn.MustDisconnect()
|
||||||
|
stompConn, err = cfg.DialStomp(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("DialStomp failed as well :(")
|
||||||
|
// Try again later.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer stompConn.Disconnect()
|
||||||
|
|
||||||
|
sub, err = cfg.StompSubscribe(ctx, stompConn)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("StompSubscribe with new connection failed: %v", err)
|
||||||
|
// Try again later.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer sub.Unsubscribe()
|
||||||
|
mustReconnect = false
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case m, ok := <-sub.C:
|
||||||
|
if !ok {
|
||||||
|
mustReconnect = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
d, err := unmarshalGzipPushPort(m.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("unmarshalGzipPushPort: %v", err)
|
||||||
|
if err := stompConn.Nack(m); err != nil {
|
||||||
|
log.Printf("attempting to nack unparsable message: %v", err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := handleMsg(cfg.Message)(d); err != nil {
|
||||||
|
log.Printf("Message: %v", err)
|
||||||
|
if err := stompConn.Nack(m); err != nil {
|
||||||
|
log.Printf("attempting to nack unhandled message: %v", err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := stompConn.Ack(m); err != nil {
|
||||||
|
log.Printf("attempting to ack handled message: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
stompBackoff.Reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
169
go/trains/darwin/darwingest/darwingestftp/darwingestftp.go
Normal file
169
go/trains/darwin/darwingest/darwingestftp/darwingestftp.go
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
package darwingestftp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"compress/gzip"
|
||||||
|
"context"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jlaffaye/ftp"
|
||||||
|
"hg.lukegb.com/lukegb/depot/go/trains/darwin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadGzippedFile(ctx context.Context, sc *ftp.ServerConn, fn string, cb func(pp *darwin.PushPort) error) error {
|
||||||
|
log.Printf("fetching %v", fn)
|
||||||
|
resp, err := sc.Retr(fn)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Retr(%q): %w", fn, err)
|
||||||
|
}
|
||||||
|
defer resp.Close()
|
||||||
|
|
||||||
|
respGz, err := gzip.NewReader(resp)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("gzip.NewReader from %q: %w", fn, err)
|
||||||
|
}
|
||||||
|
defer respGz.Close()
|
||||||
|
|
||||||
|
xmlDec := xml.NewDecoder(respGz)
|
||||||
|
for {
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
var pp darwin.PushPort
|
||||||
|
if err := xmlDec.Decode(&pp); errors.Is(err, io.EOF) {
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
return fmt.Errorf("xmlDec.Decode from %q: %w", fn, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cb(&pp); err != nil {
|
||||||
|
return fmt.Errorf("processing %q: %w", fn, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadSnapshot(ctx context.Context, sc *ftp.ServerConn, cb func(*darwin.PushPort) error) (lowWater, highWater time.Time, err error) {
|
||||||
|
log.Println("fetching snapshot...")
|
||||||
|
err = loadGzippedFile(ctx, sc, "/snapshot/snapshot.gz", func(pp *darwin.PushPort) error {
|
||||||
|
if lowWater.IsZero() || lowWater.After(pp.Time) {
|
||||||
|
lowWater = pp.Time
|
||||||
|
}
|
||||||
|
if highWater.IsZero() || highWater.Before(pp.Time) {
|
||||||
|
highWater = pp.Time
|
||||||
|
}
|
||||||
|
return cb(pp)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, time.Time{}, err
|
||||||
|
}
|
||||||
|
return lowWater, highWater, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type logFTPFile struct {
|
||||||
|
Filename string
|
||||||
|
EndAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadLogs(ctx context.Context, sc *ftp.ServerConn, startAt, endAt time.Time, cb func(*darwin.PushPort) error) error {
|
||||||
|
firstIt := true
|
||||||
|
for {
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("fetching logs starting at", startAt)
|
||||||
|
entries, err := sc.List("/pushport")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("listing /pushport: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var fs []logFTPFile
|
||||||
|
for _, e := range entries {
|
||||||
|
if e.Type != ftp.EntryTypeFile {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := time.ParseInLocation("pPortData.log.2006-01-02_1504.gz", e.Name, darwin.London)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("failed to parse filename %q: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fs = append(fs, logFTPFile{
|
||||||
|
Filename: e.Name,
|
||||||
|
EndAt: t,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(fs, func(i, j int) bool {
|
||||||
|
return fs[i].EndAt.Before(fs[j].EndAt)
|
||||||
|
})
|
||||||
|
|
||||||
|
startReadingFrom := -1
|
||||||
|
endReadingAt := len(fs) - 1
|
||||||
|
for n, f := range fs {
|
||||||
|
if f.EndAt.After(startAt) && startReadingFrom == -1 {
|
||||||
|
startReadingFrom = n
|
||||||
|
}
|
||||||
|
if f.EndAt.After(endAt) && !endAt.IsZero() {
|
||||||
|
endReadingAt = n
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var lowWater, highWater time.Time
|
||||||
|
if startReadingFrom != -1 {
|
||||||
|
for n := startReadingFrom; n <= endReadingAt; n++ {
|
||||||
|
fmt.Printf("%v: ", fs[n].Filename)
|
||||||
|
err := loadGzippedFile(ctx, sc, fmt.Sprintf("/pushport/%v", fs[n].Filename), func(pp *darwin.PushPort) error {
|
||||||
|
if lowWater.IsZero() || lowWater.After(pp.Time) {
|
||||||
|
lowWater = pp.Time
|
||||||
|
}
|
||||||
|
if highWater.IsZero() || highWater.Before(pp.Time) {
|
||||||
|
highWater = pp.Time
|
||||||
|
}
|
||||||
|
if !pp.Time.After(startAt) {
|
||||||
|
fmt.Printf("x")
|
||||||
|
return nil
|
||||||
|
} else if !endAt.IsZero() && (endAt.Before(pp.Time) || endAt.Equal(pp.Time)) {
|
||||||
|
fmt.Printf("_")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return cb(pp)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Print("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !lowWater.Before(startAt) {
|
||||||
|
// Don't believe that we saw everything :/
|
||||||
|
log.Printf("low water was %v; start at was %v; we might not have seen everything!", lowWater, startAt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !highWater.After(endAt) {
|
||||||
|
if !firstIt {
|
||||||
|
log.Printf("not yet caught up: high water %v; end at %v; waiting 1 minute...", highWater, endAt)
|
||||||
|
if err := darwin.Sleep(ctx, 1*time.Minute); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
firstIt = false
|
||||||
|
if !highWater.IsZero() {
|
||||||
|
startAt = highWater
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
17
go/trains/darwin/darwingest/darwingestftp/default.nix
Normal file
17
go/trains/darwin/darwingest/darwingestftp/default.nix
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }@args:
|
||||||
|
depot.third_party.buildGo.package {
|
||||||
|
name = "darwingestftp";
|
||||||
|
srcs = [
|
||||||
|
./darwingestftp.go
|
||||||
|
];
|
||||||
|
path = "hg.lukegb.com/lukegb/depot/go/trains/darwin/darwingest/darwingestftp";
|
||||||
|
deps = with depot.third_party; [
|
||||||
|
gopkgs."github.com".jlaffaye.ftp
|
||||||
|
|
||||||
|
depot.go.trains.darwin
|
||||||
|
];
|
||||||
|
}
|
214
go/trains/darwin/darwingest/darwingests3/darwingests3.go
Normal file
214
go/trains/darwin/darwingest/darwingests3/darwingests3.go
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
package darwingests3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"compress/gzip"
|
||||||
|
"context"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gocloud.dev/blob"
|
||||||
|
"hg.lukegb.com/lukegb/depot/go/trains/darwin"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
blobRegex = regexp.MustCompile(`^(?P<datetime>[0-9]{14})(_ref)?_v(?P<version>[0-9]+)\.xml\.gz$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
understoodVersionTimetable = 8
|
||||||
|
understoodVersionReference = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
type fileType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
fileTypeWantAny fileType = iota
|
||||||
|
fileTypeTimetable
|
||||||
|
fileTypeReference
|
||||||
|
)
|
||||||
|
|
||||||
|
func (ft fileType) IsA(wantFileType fileType) bool {
|
||||||
|
switch wantFileType {
|
||||||
|
case fileTypeWantAny:
|
||||||
|
return true
|
||||||
|
case ft:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type fileInfo struct {
|
||||||
|
filename string
|
||||||
|
fileType fileType
|
||||||
|
version int
|
||||||
|
filenameDate time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseFilename(fn string) (fileInfo, error) {
|
||||||
|
m := blobRegex.FindStringSubmatch(fn)
|
||||||
|
if m == nil {
|
||||||
|
return fileInfo{}, fmt.Errorf("couldn't parse filename %q: regex didn't match", fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
filenameDateStr := m[1]
|
||||||
|
filenameDate, err := time.ParseInLocation("20060102150405", filenameDateStr, darwin.London)
|
||||||
|
if err != nil {
|
||||||
|
return fileInfo{}, fmt.Errorf("couldn't parse blob filename %q: datetime part %q unparsable: %v", fn, filenameDateStr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
versionStr := m[3]
|
||||||
|
version, err := strconv.Atoi(versionStr)
|
||||||
|
if err != nil {
|
||||||
|
return fileInfo{}, fmt.Errorf("couldn't parse blob filename %q: version part %q unparsable: %v", fn, versionStr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileType := fileTypeTimetable
|
||||||
|
if m[2] != "" {
|
||||||
|
fileType = fileTypeReference
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileInfo{
|
||||||
|
filename: fn,
|
||||||
|
fileType: fileType,
|
||||||
|
version: version,
|
||||||
|
filenameDate: filenameDate,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func filesInBucket(ctx context.Context, bucket *blob.Bucket, wantFileType fileType) ([]fileInfo, error) {
|
||||||
|
var fis []fileInfo
|
||||||
|
|
||||||
|
iter := bucket.List(nil)
|
||||||
|
for {
|
||||||
|
obj, err := iter.Next(ctx)
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("listing objects in bucket: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := parseFilename(obj.Key)
|
||||||
|
if err != nil {
|
||||||
|
log.Print(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !fi.fileType.IsA(wantFileType) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fis = append(fis, fi)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fis, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadTimetableForDate(ctx context.Context, bucket *blob.Bucket, ts time.Time) (*darwin.PushPortTimetable, error) {
|
||||||
|
// Find the latest filename that covers "today".
|
||||||
|
// This is a little tricky, and probably wrong, but we'll try and be good enough.
|
||||||
|
fis, err := filesInBucket(ctx, bucket, fileTypeTimetable)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("filesInBucket: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OK, find one where the date matches...
|
||||||
|
wanty, wantm, wantd := ts.Date()
|
||||||
|
var got *fileInfo
|
||||||
|
for _, fi := range fis {
|
||||||
|
if fi.version != understoodVersionTimetable {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fiy, fim, fid := fi.filenameDate.Date()
|
||||||
|
if wanty == fiy && wantm == fim && wantd == fid {
|
||||||
|
fi := fi
|
||||||
|
got = &fi
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if got == nil {
|
||||||
|
return nil, fmt.Errorf("unable to find matching timetable for %v")
|
||||||
|
}
|
||||||
|
|
||||||
|
return LoadTimetable(ctx, bucket, got.filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeFromBucket(ctx context.Context, bucket *blob.Bucket, filename string, out interface{}) error {
|
||||||
|
r, err := bucket.NewReader(ctx, filename, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("NewReader(%q): %w", filename, err)
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
gzr, err := gzip.NewReader(r)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("gzip.NewReader(fn=%q): %w", filename, err)
|
||||||
|
}
|
||||||
|
defer gzr.Close()
|
||||||
|
|
||||||
|
if err := xml.NewDecoder(gzr).Decode(out); err != nil {
|
||||||
|
return fmt.Errorf("decoding XML from %q: %w", filename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrBadVersion = errors.New("darwingests3: bad version, ignoring request")
|
||||||
|
|
||||||
|
func LoadTimetable(ctx context.Context, bucket *blob.Bucket, filename string) (*darwin.PushPortTimetable, error) {
|
||||||
|
log.Printf("loading timetable from S3 file %q", filename)
|
||||||
|
fi, err := parseFilename(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if fi.version != understoodVersionTimetable {
|
||||||
|
return nil, ErrBadVersion
|
||||||
|
}
|
||||||
|
var ppt darwin.PushPortTimetable
|
||||||
|
if err := decodeFromBucket(ctx, bucket, filename, &ppt); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &ppt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadLatestReferenceData(ctx context.Context, bucket *blob.Bucket) (*darwin.PushPortReferenceData, error) {
|
||||||
|
fis, err := filesInBucket(ctx, bucket, fileTypeReference)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("filesInBucket: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(fis, func(i, j int) bool {
|
||||||
|
if fis[i].version == understoodVersionReference && fis[j].version != understoodVersionReference {
|
||||||
|
return true
|
||||||
|
} else if fis[i].version != understoodVersionReference && fis[j].version == understoodVersionReference {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return fis[i].filenameDate.Before(fis[j].filenameDate)
|
||||||
|
})
|
||||||
|
|
||||||
|
return LoadReferenceData(ctx, bucket, fis[0].filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadReferenceData(ctx context.Context, bucket *blob.Bucket, filename string) (*darwin.PushPortReferenceData, error) {
|
||||||
|
log.Printf("loading reference data from S3 file %q", filename)
|
||||||
|
fi, err := parseFilename(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if fi.version != understoodVersionReference {
|
||||||
|
return nil, ErrBadVersion
|
||||||
|
}
|
||||||
|
var pprd darwin.PushPortReferenceData
|
||||||
|
if err := decodeFromBucket(ctx, bucket, filename, &pprd); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &pprd, nil
|
||||||
|
}
|
17
go/trains/darwin/darwingest/darwingests3/default.nix
Normal file
17
go/trains/darwin/darwingest/darwingests3/default.nix
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }@args:
|
||||||
|
depot.third_party.buildGo.package {
|
||||||
|
name = "darwingests3";
|
||||||
|
srcs = [
|
||||||
|
./darwingests3.go
|
||||||
|
];
|
||||||
|
path = "hg.lukegb.com/lukegb/depot/go/trains/darwin/darwingest/darwingests3";
|
||||||
|
deps = with depot.third_party; [
|
||||||
|
gopkgs."gocloud.dev".blob
|
||||||
|
|
||||||
|
depot.go.trains.darwin
|
||||||
|
];
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package darwingeststomp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
stomp "github.com/go-stomp/stomp/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Hostname string
|
||||||
|
Username, Password string
|
||||||
|
ClientID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func Dial(ctx context.Context, cfg Config) (*stomp.Conn, error) {
|
||||||
|
const maxExpectedQPS = 60
|
||||||
|
var maxExpectedDowntime = int((10 * time.Minute).Seconds())
|
||||||
|
return stomp.Dial(
|
||||||
|
"tcp", cfg.Hostname,
|
||||||
|
stomp.ConnOpt.Login(cfg.Username, cfg.Password),
|
||||||
|
stomp.ConnOpt.Header("client-id", cfg.ClientID),
|
||||||
|
stomp.ConnOpt.ReadChannelCapacity(maxExpectedQPS*maxExpectedDowntime),
|
||||||
|
stomp.ConnOpt.HeartBeat(7*time.Minute, 1*time.Minute),
|
||||||
|
stomp.ConnOpt.HeartBeatGracePeriodMultiplier(3),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Subscribe(c *stomp.Conn, subName string) (*stomp.Subscription, error) {
|
||||||
|
return c.Subscribe(
|
||||||
|
"/topic/darwin.pushport-v16", stomp.AckClientIndividual,
|
||||||
|
stomp.SubscribeOpt.Header("durable-subscription-name", subName),
|
||||||
|
)
|
||||||
|
}
|
17
go/trains/darwin/darwingest/darwingeststomp/default.nix
Normal file
17
go/trains/darwin/darwingest/darwingeststomp/default.nix
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }@args:
|
||||||
|
depot.third_party.buildGo.package {
|
||||||
|
name = "darwingeststomp";
|
||||||
|
srcs = [
|
||||||
|
./darwingeststomp.go
|
||||||
|
];
|
||||||
|
path = "hg.lukegb.com/lukegb/depot/go/trains/darwin/darwingest/darwingeststomp";
|
||||||
|
deps = with depot.third_party; [
|
||||||
|
gopkgs."github.com".go-stomp.stomp.v3
|
||||||
|
|
||||||
|
depot.go.trains.darwin
|
||||||
|
];
|
||||||
|
}
|
27
go/trains/darwin/darwingest/default.nix
Normal file
27
go/trains/darwin/darwingest/default.nix
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }@args:
|
||||||
|
depot.third_party.buildGo.package {
|
||||||
|
name = "darwingest";
|
||||||
|
srcs = [
|
||||||
|
./darwingest.go
|
||||||
|
];
|
||||||
|
path = "hg.lukegb.com/lukegb/depot/go/trains/darwin/darwingest";
|
||||||
|
deps = with depot.third_party; [
|
||||||
|
gopkgs."github.com".cenkalti.backoff.v4
|
||||||
|
gopkgs."github.com".go-stomp.stomp.v3
|
||||||
|
gopkgs."github.com".jlaffaye.ftp
|
||||||
|
gopkgs."gocloud.dev".blob
|
||||||
|
|
||||||
|
depot.go.trains.darwin
|
||||||
|
depot.go.trains.darwin.darwingest.darwingestftp
|
||||||
|
depot.go.trains.darwin.darwingest.darwingests3
|
||||||
|
depot.go.trains.darwin.darwingest.darwingeststomp
|
||||||
|
];
|
||||||
|
} // {
|
||||||
|
darwingestftp = import ./darwingestftp args;
|
||||||
|
darwingests3 = import ./darwingests3 args;
|
||||||
|
darwingeststomp = import ./darwingeststomp args;
|
||||||
|
}
|
18
go/trains/darwin/default.nix
Normal file
18
go/trains/darwin/default.nix
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }@args:
|
||||||
|
depot.third_party.buildGo.package {
|
||||||
|
name = "darwin";
|
||||||
|
srcs = [
|
||||||
|
./msg.go
|
||||||
|
./refdata.go
|
||||||
|
./timezone.go
|
||||||
|
./util.go
|
||||||
|
];
|
||||||
|
path = "hg.lukegb.com/lukegb/depot/go/trains/darwin";
|
||||||
|
} // {
|
||||||
|
darwindb = import ./darwindb args;
|
||||||
|
darwingest = import ./darwingest args;
|
||||||
|
}
|
436
go/trains/darwin/msg.go
Normal file
436
go/trains/darwin/msg.go
Normal file
|
@ -0,0 +1,436 @@
|
||||||
|
package darwin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QueryTimetable struct {
|
||||||
|
//XMLName xml.Name `xml:"QueryTimetable"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TimetableID struct {
|
||||||
|
//XMLName xml.Name `xml:"TimeTableId"`
|
||||||
|
ID string `xml:",chardata"`
|
||||||
|
Filename string `xml:"ttfile,attr"`
|
||||||
|
ReferenceFilename string `xml:"ttreffile,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetSnapshotReq struct {
|
||||||
|
//XMLName xml.Name `xml:"GetSnapshotReq"`
|
||||||
|
ViaFTP bool `xml:"viaftp,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetFullSnapshotReq struct {
|
||||||
|
//XMLName xml.Name `xml:"GetFullSnapshotReq"`
|
||||||
|
ViaFTP bool `xml:"viaftp,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SnapshotID struct {
|
||||||
|
//XMLName xml.Name `xml:"SnapshotId"`
|
||||||
|
ID string `xml:",chardata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StartUpdateReq struct {
|
||||||
|
//XMLName xml.Name `xml:"StartUpdateReq"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StopUpdateReq struct {
|
||||||
|
//XMLName xml.Name `xml:"StopUpdateReq"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FailureResp struct {
|
||||||
|
//XMLName xml.Name `xml:"FailureResp"`
|
||||||
|
ErrorMessage string `xml:",chardata"`
|
||||||
|
Code string `xml:"code,attr"`
|
||||||
|
RequestSource string `xml:"requestSource,attr"`
|
||||||
|
RequestID string `xml:"requestID,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScheduleCallingPoint struct {
|
||||||
|
XMLName xml.Name
|
||||||
|
|
||||||
|
TIPLOC string `xml:"tpl,attr"`
|
||||||
|
Act string `xml:"act,attr"`
|
||||||
|
PlanAct string `xml:"planAct,attr"`
|
||||||
|
Platform string `xml:"plat,attr"`
|
||||||
|
Cancelled bool `xml:"can,attr"`
|
||||||
|
FID string `xml:"fid,attr"`
|
||||||
|
|
||||||
|
PTA string `xml:"pta,attr"`
|
||||||
|
PTD string `xml:"ptd,attr"`
|
||||||
|
AvgLoading int `xml:"avgLoading,attr"`
|
||||||
|
|
||||||
|
WTA string `xml:"wta,attr"`
|
||||||
|
WTD string `xml:"wtd,attr"`
|
||||||
|
WTP string `xml:"wtp,attr"`
|
||||||
|
FD string `xml:"fd,attr"`
|
||||||
|
|
||||||
|
RDelay int `xml:"rdelay,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Schedule struct {
|
||||||
|
//XMLName xml.Name `xml:"schedule"`
|
||||||
|
|
||||||
|
CallingPoints []ScheduleCallingPoint `xml:",any"`
|
||||||
|
CancellationReason *DisruptionReason `xml:"cancelReason" json:",omitempty"`
|
||||||
|
|
||||||
|
RID string `xml:"rid,attr"`
|
||||||
|
UID string `xml:"uid,attr"`
|
||||||
|
TrainID string `xml:"trainId,attr"`
|
||||||
|
RSID string `xml:"rsid,attr"`
|
||||||
|
SSD string `xml:"ssd,attr"` // RTTIDateType
|
||||||
|
TOC string `xml:"toc,attr"`
|
||||||
|
Status *string `xml:"status,attr"`
|
||||||
|
TrainCat *string `xml:"trainCat,attr"`
|
||||||
|
IsPassengerSvc *bool `xml:"isPassengerSvc,attr"`
|
||||||
|
IsActive *bool `xml:"isActive,attr"`
|
||||||
|
Deleted bool `xml:"deleted,attr"`
|
||||||
|
IsCharter *bool `xml:"isCharter,attr"`
|
||||||
|
IsQTrain *bool `xml:"qtrain,attr"`
|
||||||
|
Cancelled *bool `xml:"can,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Schedule) AffectedRIDs() []string {
|
||||||
|
return []string{s.RID}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeactivatedSchedule struct {
|
||||||
|
//XMLName xml.Name `xml:"deactivated"`
|
||||||
|
|
||||||
|
RID string `xml:"rid,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ds DeactivatedSchedule) AffectedRIDs() []string {
|
||||||
|
return []string{ds.RID}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CircularTimes struct {
|
||||||
|
WTA string `xml:"wta,attr" json:",omitempty"`
|
||||||
|
WTD string `xml:"wtd,attr" json:",omitempty"`
|
||||||
|
WTP string `xml:"wtp,attr" json:",omitempty"`
|
||||||
|
PTA string `xml:"pta,attr" json:",omitempty"`
|
||||||
|
PTD string `xml:"ptd,attr" json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AssocService struct {
|
||||||
|
RID string `xml:"rid,attr"`
|
||||||
|
CircularTimes
|
||||||
|
}
|
||||||
|
|
||||||
|
type Association struct {
|
||||||
|
//XMLName xml.Name `xml:"association"`
|
||||||
|
|
||||||
|
Main AssocService `xml:"main"`
|
||||||
|
Assoc AssocService `xml:"assoc"`
|
||||||
|
|
||||||
|
TIPLOC string `xml:"tiploc,attr"`
|
||||||
|
Category string `xml:"category,attr"`
|
||||||
|
IsCancelled bool `xml:"isCancelled,attr"`
|
||||||
|
IsDeleted bool `xml:"isDeleted,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Association) AffectedRIDs() []string {
|
||||||
|
return []string{a.Main.RID, a.Assoc.RID}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ToiletAvailability struct {
|
||||||
|
Type string `xml:",chardata"`
|
||||||
|
Status string `xml:"status,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Coach struct {
|
||||||
|
Toilet []ToiletAvailability `xml:"toilet"`
|
||||||
|
|
||||||
|
CoachNumber string `xml:"coachNumber,attr"`
|
||||||
|
CoachClass string `xml:"coachClass,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Formation struct {
|
||||||
|
//XMLName xml.Name `xml:"formation"`
|
||||||
|
|
||||||
|
Coaches []Coach `xml:"coaches>coach"`
|
||||||
|
|
||||||
|
FID string `xml:"fid,attr"`
|
||||||
|
Src string `xml:"src,attr"`
|
||||||
|
SrcInst string `xml:"srcInst,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScheduleFormation struct {
|
||||||
|
//XMLName xml.Name `xml:"scheduleFormations"`
|
||||||
|
|
||||||
|
Formation []Formation `xml:"formation"`
|
||||||
|
|
||||||
|
RID string `xml:"rid,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf ScheduleFormation) AffectedRIDs() []string {
|
||||||
|
return []string{sf.RID}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DisruptionReason struct {
|
||||||
|
Code string `xml:",chardata"`
|
||||||
|
TIPLOC string `xml:"tiploc,attr"`
|
||||||
|
Near bool `xml:"near,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TSTimeData struct {
|
||||||
|
ET string `xml:"et,attr" json:",omitempty"`
|
||||||
|
WET string `xml:"wet,attr" json:",omitempty"`
|
||||||
|
AT string `xml:"at,attr" json:",omitempty"`
|
||||||
|
ATRemoved bool `xml:"atRemoved,attr" json:",omitempty"`
|
||||||
|
ETMin string `xml:"etmin,attr" json:",omitempty"`
|
||||||
|
ETUnknown bool `xml:"etUnknown,attr" json:",omitempty"`
|
||||||
|
Delayed bool `xml:"delayed,attr" json:",omitempty"`
|
||||||
|
Src string `xml:"src,attr" json:",omitempty"`
|
||||||
|
SrcInst string `xml:"srcInst,attr" json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlatformData struct {
|
||||||
|
Platform string `xml:",chardata"`
|
||||||
|
PlatSup bool `xml:"platsup,attr"`
|
||||||
|
CisPlatSup bool `xml:"cisPlatsup,attr"`
|
||||||
|
PlatSrc string `xml:"platsrc,attr"`
|
||||||
|
Conf bool `xml:"conf,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TSLocation struct {
|
||||||
|
TIPLOC string `xml:"tpl,attr"`
|
||||||
|
CircularTimes
|
||||||
|
|
||||||
|
Arr *TSTimeData `xml:"arr" json:",omitempty"`
|
||||||
|
Dep *TSTimeData `xml:"dep" json:",omitempty"`
|
||||||
|
Pass *TSTimeData `xml:"pass" json:",omitempty"`
|
||||||
|
Plat *PlatformData `xml:"plat" json:",omitempty"`
|
||||||
|
Suppr *bool `xml:"suppr" json:",omitempty"`
|
||||||
|
Length *int `xml:"length" json:",omitempty"`
|
||||||
|
DetachFront *bool `xml:"detachFront" json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TrainStatus struct {
|
||||||
|
//XMLName xml.Name `xml:"TS"`
|
||||||
|
|
||||||
|
LateReason *DisruptionReason `xml:"LateReason" json:",omitempty"`
|
||||||
|
Location []TSLocation `xml:"Location"`
|
||||||
|
|
||||||
|
RID string `xml:"rid,attr"`
|
||||||
|
UID string `xml:"uid,attr"`
|
||||||
|
SSD string `xml:"ssd,attr"`
|
||||||
|
IsReverseFormation bool `xml:"isReverseFormation,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts TrainStatus) AffectedRIDs() []string {
|
||||||
|
return []string{ts.RID}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CoachLoadingData struct {
|
||||||
|
Value string `xml:",chardata"`
|
||||||
|
|
||||||
|
CoachNumber string `xml:"coachNumber,attr"`
|
||||||
|
Src string `xml:"src,attr"`
|
||||||
|
SrcInst string `xml:"srcInst,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FormationLoading struct {
|
||||||
|
//XMLName xml.Name `xml:"formationLoading"`
|
||||||
|
|
||||||
|
Loading []CoachLoadingData `xml:"loading"`
|
||||||
|
|
||||||
|
FID string `xml:"fid,attr"`
|
||||||
|
RID string `xml:"rid,attr"`
|
||||||
|
TIPLOC string `xml:"tpl,attr"`
|
||||||
|
CircularTimes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fl FormationLoading) AffectedRIDs() []string {
|
||||||
|
return []string{fl.RID}
|
||||||
|
}
|
||||||
|
|
||||||
|
type StationMessageStation struct {
|
||||||
|
CRS string `xml:"crs,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StationMessageMessage struct {
|
||||||
|
InnerXML string `xml:",innerxml"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StationMessage struct {
|
||||||
|
Station []StationMessageStation `xml:"Station"`
|
||||||
|
Msg StationMessageMessage `xml:"Msg"`
|
||||||
|
|
||||||
|
ID int `xml:"id,attr"`
|
||||||
|
Cat string `xml:"cat,attr"`
|
||||||
|
Sev string `xml:"sev,attr"`
|
||||||
|
Suppress bool `xml:"bool,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AlertService struct {
|
||||||
|
RID string `xml:"RID,attr"`
|
||||||
|
UID string `xml:"UID,attr"`
|
||||||
|
SSD string `xml:"SSD,attr"`
|
||||||
|
|
||||||
|
Location []string `xml:"Location"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TrainAlert struct {
|
||||||
|
AlertID int `xml:"AlertID"`
|
||||||
|
AlertServices []AlertService `xml:"AlertServices"`
|
||||||
|
SendAlertBySMS bool `xml:"SendAlertBySMS"`
|
||||||
|
SendAlertByEmail bool `xml:"SendAlertByEmail"`
|
||||||
|
SendAlertByTwitter bool `xml:"SendAlertByTwitter"`
|
||||||
|
Source string `xml:"Source"`
|
||||||
|
AlertText string `xml:"AlertText"`
|
||||||
|
Audience string `xml:"Audience"`
|
||||||
|
AlertType string `xml:"AlertType"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ta TrainAlert) AffectedRIDs() []string {
|
||||||
|
rids := make([]string, len(ta.AlertServices))
|
||||||
|
for n, as := range ta.AlertServices {
|
||||||
|
rids[n] = as.RID
|
||||||
|
}
|
||||||
|
return rids
|
||||||
|
}
|
||||||
|
|
||||||
|
type TrainOrderRID struct {
|
||||||
|
RID string `xml:",chardata"`
|
||||||
|
CircularTimes
|
||||||
|
}
|
||||||
|
|
||||||
|
type TrainOrderItem struct {
|
||||||
|
RID *TrainOrderRID `xml:"rid"`
|
||||||
|
TrainID *string `xml:"trainID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TrainOrderData struct {
|
||||||
|
First TrainOrderItem `xml:"first"`
|
||||||
|
Second *TrainOrderItem `xml:"second"`
|
||||||
|
Third *TrainOrderItem `xml:"third"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TrainOrder struct {
|
||||||
|
Set *TrainOrderData `xml:"set"`
|
||||||
|
Clear struct{} `xml:"clear"`
|
||||||
|
|
||||||
|
TIPLOC string `xml:"tiploc,attr"`
|
||||||
|
CRS string `xml:"crs,attr"`
|
||||||
|
Platform string `xml:"platform,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FullTDBerthID struct {
|
||||||
|
Berth string `xml:",chardata"`
|
||||||
|
|
||||||
|
Area string `xml:"area,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TrackingID struct {
|
||||||
|
Berth FullTDBerthID `xml:"berth"`
|
||||||
|
IncorrectTrainID string `xml:"incorrectTrainID"`
|
||||||
|
CorrectTrainID string `xml:"correctTrainID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RTTIAlarmData struct {
|
||||||
|
TDAreaFail *string `xml:"tdAreaFail"`
|
||||||
|
TDFeedFail *string `xml:"tdFeedFail"`
|
||||||
|
TyrellFeedFail *string `xml:"tyrellFeedFail"`
|
||||||
|
|
||||||
|
ID string `xml:"id,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RTTIAlarm struct {
|
||||||
|
Set *RTTIAlarmData `xml:"set"`
|
||||||
|
Clear *string `xml:"clear"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DataResponse struct {
|
||||||
|
Schedule []Schedule `xml:"schedule" json:",omitempty"`
|
||||||
|
Deactivated []DeactivatedSchedule `xml:"deactivated" json:",omitempty"`
|
||||||
|
Association []Association `xml:"association" json:",omitempty"`
|
||||||
|
ScheduleFormations []ScheduleFormation `xml:"scheduleFormations" json:",omitempty"`
|
||||||
|
TrainStatus []TrainStatus `xml:"TS" json:",omitempty"`
|
||||||
|
FormationLoading []FormationLoading `xml:"formationLoading" json:",omitempty"`
|
||||||
|
OW []StationMessage `xml:"OW" json:",omitempty"`
|
||||||
|
TrainAlert []TrainAlert `xml:"trainAlert" json:",omitempty"`
|
||||||
|
TrainOrder []TrainOrder `xml:"trainOrder" json:",omitempty"`
|
||||||
|
TrackingID []TrackingID `xml:"trackingID" json:",omitempty"`
|
||||||
|
Alarm []RTTIAlarm `xml:"alarm" json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r DataResponse) AffectedRIDs() []string {
|
||||||
|
var rids []string
|
||||||
|
for _, s := range r.Schedule {
|
||||||
|
rids = append(rids, s.AffectedRIDs()...)
|
||||||
|
}
|
||||||
|
for _, ds := range r.Deactivated {
|
||||||
|
rids = append(rids, ds.AffectedRIDs()...)
|
||||||
|
}
|
||||||
|
for _, a := range r.Association {
|
||||||
|
rids = append(rids, a.AffectedRIDs()...)
|
||||||
|
}
|
||||||
|
for _, sf := range r.ScheduleFormations {
|
||||||
|
rids = append(rids, sf.AffectedRIDs()...)
|
||||||
|
}
|
||||||
|
for _, ts := range r.TrainStatus {
|
||||||
|
rids = append(rids, ts.AffectedRIDs()...)
|
||||||
|
}
|
||||||
|
for _, fl := range r.FormationLoading {
|
||||||
|
rids = append(rids, fl.AffectedRIDs()...)
|
||||||
|
}
|
||||||
|
// OW doesn't affect RIDs directly.
|
||||||
|
for _, ta := range r.TrainAlert {
|
||||||
|
rids = append(rids, ta.AffectedRIDs()...)
|
||||||
|
}
|
||||||
|
// TrainOrder needs extra processing (clear doesn't specify anything so we have to know ahead of time...)
|
||||||
|
// TrackingID doesn't contain RIDs.
|
||||||
|
// Alarm doesn't affect RIDs directly.
|
||||||
|
return rids
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateResp struct {
|
||||||
|
UpdateOrigin string `xml:"updateOrigin,attr"`
|
||||||
|
RequestSource string `xml:"requestSource,attr" json:",omitempty"`
|
||||||
|
RequestID string `xml:"requestID,attr" json:",omitempty"`
|
||||||
|
DataResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
type SnapshotResp struct {
|
||||||
|
DataResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
type PushPort struct {
|
||||||
|
//XMLName xml.Name `xml:"Pport"`
|
||||||
|
|
||||||
|
// Subelements
|
||||||
|
QueryTimetable []QueryTimetable `xml:"QueryTimetable" json:",omitempty"`
|
||||||
|
TimeTableID []TimetableID `xml:"TimeTableId" json:",omitempty"`
|
||||||
|
GetSnapshotReq []GetSnapshotReq `xml:"GetSnapshotReq" json:",omitempty"`
|
||||||
|
GetFullSnapshotReq []GetFullSnapshotReq `xml:"GetFullSnapshotReq" json:",omitempty"`
|
||||||
|
SnapshotID []SnapshotID `xml:"SnapshotId" json:",omitempty"`
|
||||||
|
StartUpdateReq []StartUpdateReq `xml:"StartUpdateReq" json:",omitempty"`
|
||||||
|
StopUpdateReq []StopUpdateReq `xml:"StopUpdateReq" json:",omitempty"`
|
||||||
|
FailureResp []FailureResp `xml:"FailureResp" json:",omitempty"`
|
||||||
|
UpdateResp []UpdateResp `xml:"uR" json:",omitempty"`
|
||||||
|
SnapshotResp []SnapshotResp `xml:"sR" json:",omitempty"`
|
||||||
|
|
||||||
|
// Attrs
|
||||||
|
Time time.Time `xml:"ts,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp PushPort) AffectedRIDs() []string {
|
||||||
|
var rids []string
|
||||||
|
for _, r := range pp.UpdateResp {
|
||||||
|
rids = append(rids, r.AffectedRIDs()...)
|
||||||
|
}
|
||||||
|
for _, r := range pp.SnapshotResp {
|
||||||
|
rids = append(rids, r.AffectedRIDs()...)
|
||||||
|
}
|
||||||
|
return rids
|
||||||
|
}
|
||||||
|
|
||||||
|
type PushPortTimetable struct {
|
||||||
|
Journey []Schedule `xml:"Journey"`
|
||||||
|
Association []Association `xml:"Association"`
|
||||||
|
|
||||||
|
// Attrs
|
||||||
|
TimetableID string `xml:"timetableID,attr"`
|
||||||
|
}
|
45
go/trains/darwin/refdata.go
Normal file
45
go/trains/darwin/refdata.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package darwin
|
||||||
|
|
||||||
|
import "encoding/xml"
|
||||||
|
|
||||||
|
type RefLocationRef struct {
|
||||||
|
TIPLOC string `xml:"tpl,attr"`
|
||||||
|
Name string `xml:"locname,attr"`
|
||||||
|
CRS *string `xml:"crs,attr"`
|
||||||
|
TOC *string `xml:"toc,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RefTOCRef struct {
|
||||||
|
TOC string `xml:"toc,attr"`
|
||||||
|
Name string `xml:"tocname,attr"`
|
||||||
|
URL *string `xml:"url,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RefReason struct {
|
||||||
|
Code string `xml:"code,attr"`
|
||||||
|
Text string `xml:"reasontext,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RefVia struct {
|
||||||
|
AtCRS string `xml:"at,attr"`
|
||||||
|
DestTIPLOC string `xml:"dest,attr"`
|
||||||
|
Loc1TIPLOC string `xml:"loc1,attr"`
|
||||||
|
Loc2TIPLOC string `xml:"loc2,attr"`
|
||||||
|
Text string `xml:"viatext,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RefCISSource struct {
|
||||||
|
CISCode string `xml:"code,attr"`
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PushPortReferenceData struct {
|
||||||
|
XMLName xml.Name `xml:"PportTimetableRef"`
|
||||||
|
|
||||||
|
Locations []RefLocationRef `xml:"LocationRef"`
|
||||||
|
TOCs []RefTOCRef `xml:"TocRef"`
|
||||||
|
LateRunningReasons []RefReason `xml:"LateRunningReasons>Reason"`
|
||||||
|
CancellationReasons []RefReason `xml:"CancellationReasons>Reason"`
|
||||||
|
Via []RefVia `xml:"Via"`
|
||||||
|
CISSource []RefCISSource `xml:"CISSource"`
|
||||||
|
}
|
15
go/trains/darwin/timezone.go
Normal file
15
go/trains/darwin/timezone.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package darwin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
London = func() *time.Location {
|
||||||
|
tz, err := time.LoadLocation("Europe/London")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return tz
|
||||||
|
}()
|
||||||
|
)
|
18
go/trains/darwin/util.go
Normal file
18
go/trains/darwin/util.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package darwin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Sleep(ctx context.Context, d time.Duration) error {
|
||||||
|
t := time.NewTicker(d)
|
||||||
|
defer t.Stop()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-t.C:
|
||||||
|
return nil
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
11
go/trains/default.nix
Normal file
11
go/trains/default.nix
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }@args:
|
||||||
|
|
||||||
|
{
|
||||||
|
cmd = import ./cmd args;
|
||||||
|
webapi = import ./webapi args;
|
||||||
|
darwin = import ./darwin args;
|
||||||
|
}
|
38
go/trains/go.mod
Normal file
38
go/trains/go.mod
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
module hg.lukegb.com/lukegb/depot/go/trains
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999 // indirect
|
||||||
|
github.com/cenkalti/backoff/v4 v4.1.1
|
||||||
|
github.com/coreos/bbolt v1.3.1-coreos.6 // indirect
|
||||||
|
github.com/coreos/etcd v3.3.10+incompatible // indirect
|
||||||
|
github.com/coreos/go-semver v0.2.0 // indirect
|
||||||
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
|
||||||
|
github.com/dnaeon/go-vcr v1.0.1 // indirect
|
||||||
|
github.com/go-ini/ini v1.39.0 // indirect
|
||||||
|
github.com/go-stomp/stomp/v3 v3.0.3
|
||||||
|
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7 // indirect
|
||||||
|
github.com/googleapis/gax-go v2.0.0+incompatible // indirect
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect
|
||||||
|
github.com/gorilla/context v1.1.1 // indirect
|
||||||
|
github.com/gorilla/mux v1.6.2 // indirect
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 // indirect
|
||||||
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
||||||
|
github.com/hooklift/gowsdl v0.5.0 // indirect
|
||||||
|
github.com/jackc/pgx/v4 v4.13.0
|
||||||
|
github.com/jlaffaye/ftp v0.0.0-20211029032751-b1140299f4df
|
||||||
|
github.com/jonboulle/clockwork v0.1.0 // indirect
|
||||||
|
github.com/jtolds/gls v4.2.1+incompatible // indirect
|
||||||
|
github.com/openzipkin/zipkin-go v0.1.1 // indirect
|
||||||
|
github.com/prometheus/client_golang v1.11.0
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect
|
||||||
|
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect
|
||||||
|
github.com/soheilhy/cmux v0.1.4 // indirect
|
||||||
|
github.com/streadway/amqp v0.0.0-20181107104731-27835f1a64e9 // indirect
|
||||||
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect
|
||||||
|
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 // indirect
|
||||||
|
gocloud.dev v0.24.0 // indirect
|
||||||
|
gopkg.in/ini.v1 v1.39.0 // indirect
|
||||||
|
gopkg.in/pipe.v2 v2.0.0-20140414041502-3c2ca4d52544 // indirect
|
||||||
|
)
|
959
go/trains/go.sum
Normal file
959
go/trains/go.sum
Normal file
|
@ -0,0 +1,959 @@
|
||||||
|
bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
|
||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||||
|
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||||
|
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||||
|
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||||
|
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||||
|
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||||
|
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||||
|
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||||
|
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||||
|
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||||
|
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||||
|
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||||
|
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||||
|
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||||
|
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||||
|
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||||
|
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
|
||||||
|
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
|
||||||
|
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
|
||||||
|
cloud.google.com/go v0.82.0/go.mod h1:vlKccHJGuFBFufnAnuB08dfEH9Y3H7dzDzRECFdC2TA=
|
||||||
|
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
|
||||||
|
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
|
||||||
|
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
|
||||||
|
cloud.google.com/go v0.88.0/go.mod h1:dnKwfYbP9hQhefiUvpbcAyoGSHUrOxR20JVElLiUvEY=
|
||||||
|
cloud.google.com/go v0.89.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
|
||||||
|
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
|
||||||
|
cloud.google.com/go v0.92.2/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
||||||
|
cloud.google.com/go v0.92.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
||||||
|
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
||||||
|
cloud.google.com/go v0.94.0/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
|
||||||
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
|
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||||
|
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||||
|
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||||
|
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||||
|
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||||
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
|
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||||
|
cloud.google.com/go/firestore v1.5.0/go.mod h1:c4nNYR1qdq7eaZ+jSc5fonrQN2k3M7sWATcYTiakjEo=
|
||||||
|
cloud.google.com/go/kms v0.1.0/go.mod h1:8Qp8PCAypHg4FdmlyW1QRAv09BGQ9Uzh7JnmIZxPk+c=
|
||||||
|
cloud.google.com/go/monitoring v0.1.0/go.mod h1:Hpm3XfzJv+UTiXzCG5Ffp0wijzHTC7Cv4eR7o3x/fEE=
|
||||||
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
|
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||||
|
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||||
|
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||||
|
cloud.google.com/go/pubsub v1.16.0/go.mod h1:6A8EfoWZ/lUvCWStKGwAWauJZSiuV0Mkmu6WilK/TxQ=
|
||||||
|
cloud.google.com/go/secretmanager v0.1.0/go.mod h1:3nGKHvnzDUVit7U0S9KAKJ4aOsO1xtwRG+7ey5LK1bM=
|
||||||
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
|
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||||
|
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||||
|
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||||
|
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||||
|
cloud.google.com/go/storage v1.16.1/go.mod h1:LaNorbty3ehnU3rEjXSNV/NRgQA0O8Y+uh6bPe5UOk4=
|
||||||
|
cloud.google.com/go/trace v0.1.0/go.mod h1:wxEwsoeRVPbeSkt7ZC9nWCgmoKQRAoySN7XHW2AmI7g=
|
||||||
|
contrib.go.opencensus.io/exporter/aws v0.0.0-20200617204711-c478e41e60e9/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA=
|
||||||
|
contrib.go.opencensus.io/exporter/stackdriver v0.13.8/go.mod h1:huNtlWx75MwO7qMs0KrMxPZXzNNWebav1Sq/pm02JdQ=
|
||||||
|
contrib.go.opencensus.io/integrations/ocsql v0.1.7/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE=
|
||||||
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
|
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||||
|
github.com/Azure/azure-amqp-common-go/v3 v3.1.0/go.mod h1:PBIGdzcO1teYoufTKMcGibdKaYZv4avS+O6LNIp8bq0=
|
||||||
|
github.com/Azure/azure-amqp-common-go/v3 v3.1.1/go.mod h1:YsDaPfaO9Ub2XeSKdIy2DfwuiQlHQCauHJwSqtrkECI=
|
||||||
|
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
|
||||||
|
github.com/Azure/azure-sdk-for-go v51.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||||
|
github.com/Azure/azure-sdk-for-go v57.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||||
|
github.com/Azure/azure-service-bus-go v0.10.16/go.mod h1:MlkLwGGf1ewcx5jZadn0gUEty+tTg0RaElr6bPf+QhI=
|
||||||
|
github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck=
|
||||||
|
github.com/Azure/go-amqp v0.13.0/go.mod h1:qj+o8xPCz9tMSbQ83Vp8boHahuRDl5mkNHyt1xlxUTs=
|
||||||
|
github.com/Azure/go-amqp v0.13.11/go.mod h1:D5ZrjQqB1dyp1A+G73xeL/kNn7D5qHJIIsNNps7YNmk=
|
||||||
|
github.com/Azure/go-amqp v0.13.12/go.mod h1:D5ZrjQqB1dyp1A+G73xeL/kNn7D5qHJIIsNNps7YNmk=
|
||||||
|
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||||
|
github.com/Azure/go-autorest/autorest v0.11.3/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
|
||||||
|
github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
|
||||||
|
github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
|
||||||
|
github.com/Azure/go-autorest/autorest v0.11.20/go.mod h1:o3tqFY+QR40VOlk+pV4d77mORO64jOXSgEnPQgLK6JY=
|
||||||
|
github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
|
||||||
|
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
|
||||||
|
github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk=
|
||||||
|
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
|
||||||
|
github.com/Azure/go-autorest/autorest/adal v0.9.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
|
||||||
|
github.com/Azure/go-autorest/autorest/adal v0.9.15/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A=
|
||||||
|
github.com/Azure/go-autorest/autorest/azure/auth v0.5.8/go.mod h1:kxyKZTSfKh8OVFWPAgOgQ/frrJgeYQJPyR5fLFmXko4=
|
||||||
|
github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM=
|
||||||
|
github.com/Azure/go-autorest/autorest/azure/cli v0.4.3/go.mod h1:yAQ2b6eP/CmLPnmLvxtT1ALIY3OR1oFcCqVBi8vHiTc=
|
||||||
|
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
|
||||||
|
github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||||
|
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||||
|
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
|
||||||
|
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
|
||||||
|
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||||
|
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||||
|
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/GoogleCloudPlatform/cloudsql-proxy v1.24.0/go.mod h1:3tx938GhY4FC+E1KT/jNjDw7Z5qxAEtIiERJ2sXjnII=
|
||||||
|
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||||
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
|
github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
||||||
|
github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||||
|
github.com/aws/aws-sdk-go v1.40.34 h1:SBYmodndE2d4AYucuuJnOXk4MD1SFbucoIdpwKVKeSA=
|
||||||
|
github.com/aws/aws-sdk-go v1.40.34/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
|
||||||
|
github.com/aws/aws-sdk-go-v2 v1.9.0 h1:+S+dSqQCN3MSU5vJRu1HqHrq00cJn6heIMU7X9hcsoo=
|
||||||
|
github.com/aws/aws-sdk-go-v2 v1.9.0/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.7.0 h1:J2cZ7qe+3IpqBEXnHUrFrOjoB9BlsXg7j53vxcl5IVg=
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.7.0/go.mod h1:w9+nMZ7soXCe5nT46Ri354SNhXDQ6v+V5wqDjnZE+GY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.4.0 h1:kmvesfjY861FzlCU9mvAfe01D9aeXcG2ZuC+k9F2YLM=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.4.0/go.mod h1:dgGR+Qq7Wjcd4AOAW5Rf5Tnv3+x7ed6kETXyS9WCuAY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.5.0 h1:OxTAgH8Y4BXHD6PGCJ8DHx2kaZPCQfSTqmDsdRZFezE=
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.5.0/go.mod h1:CpNzHK9VEFUCknu50kkB8z58AH2B5DvPP7ea1LHve/Y=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.2.2 h1:d95cddM3yTm4qffj3P6EnP+TzX1SSkWaQypXSgT/hpA=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.2.2/go.mod h1:BQV0agm+JEhqR+2RT5e1XTFIDcAAV0eW6z2trp+iduw=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.0 h1:VNJ5NLBteVXEwE2F1zEXVmyIH58mZ6kIQGJoC7C+vkg=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.0/go.mod h1:R1KK+vY8AfalhG1AOu5e35pOD2SdoPKQCFLTvnxiohk=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/kms v1.5.0/go.mod h1:w7JuP9Oq1IKMFQPkNe3V6s9rOssXzOVEMNEqK1L1bao=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.6.0/go.mod h1:B+7C5UKdVq1ylkI/A6O8wcurFtaux0R1njePNPtKwoA=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssm v1.10.0/go.mod h1:4dXS5YNqI3SNbetQ7X7vfsMlX6ZnboJA2dulBwJx7+g=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.4.0 h1:sHXMIKYS6YiLPzmKSvDpPmOpJDHxmAUgbiF49YNVztg=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.4.0/go.mod h1:+1fpWnL96DL23aXPpMGbsmKe8jLTEfbjuQoA4WS1VaA=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.7.0 h1:1at4e5P+lvHNl2nUktdM2/v+rpICg/QSEr9TO/uW9vU=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.7.0/go.mod h1:0qcSMCyASQPN2sk/1KQLQ2Fh6yq8wm0HSDAimPhzCoM=
|
||||||
|
github.com/aws/smithy-go v1.8.0 h1:AEwwwXQZtUwP5Mz506FeXXrKBe0jA8gVM+1gEcSRooc=
|
||||||
|
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
|
||||||
|
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
|
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
|
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||||
|
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||||
|
github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||||
|
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||||
|
github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
|
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||||
|
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
|
||||||
|
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||||
|
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||||
|
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||||
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
|
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||||
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
|
github.com/go-ini/ini v1.39.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||||
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||||
|
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||||
|
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||||
|
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/go-stomp/stomp/v3 v3.0.3 h1:7YQGJCDMkbA05Rw8dS00LxwU1mhzEHS69gMlPjMZGDk=
|
||||||
|
github.com/go-stomp/stomp/v3 v3.0.3/go.mod h1:jTrybHBK20jPdM9iyh65m6GusX6aMf7atfEFZ1nIcgc=
|
||||||
|
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||||
|
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||||
|
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||||
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||||
|
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||||
|
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||||
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||||
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||||
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-replayers/grpcreplay v1.1.0/go.mod h1:qzAvJ8/wi57zq7gWqaE6AwLM6miiXUQwP1S+I9icmhk=
|
||||||
|
github.com/google/go-replayers/httpreplay v1.0.0/go.mod h1:LJhKoTwS5Wy5Ld/peq8dFFG5OfJyHEz7ft+DsTUv25M=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
|
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
|
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
|
||||||
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20210715191844-86eeefc3e471/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||||
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
|
||||||
|
github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
|
||||||
|
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
|
||||||
|
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.1.0 h1:6DWmvNpomjL1+3liNSZbVns3zsYzzCjm6pRBO1tLeso=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
|
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
|
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hooklift/gowsdl v0.5.0 h1:DE8RevqhGPLchumV/V7OwbCzfJ8lcozFg1uWC/ESCBQ=
|
||||||
|
github.com/hooklift/gowsdl v0.5.0/go.mod h1:9kRc402w9Ci/Mek5a1DNgTmU14yPY8fMumxNVvxhis4=
|
||||||
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
||||||
|
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||||
|
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||||
|
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||||
|
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||||
|
github.com/jackc/pgconn v1.10.0 h1:4EYhlDVEMsJ30nNj0mmgwIUXoq7e9sMJrVC2ED6QlCU=
|
||||||
|
github.com/jackc/pgconn v1.10.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||||
|
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||||
|
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
|
||||||
|
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||||
|
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
||||||
|
github.com/jackc/pgtype v1.8.1 h1:9k0IXtdJXHJbyAWQgbWr1lU+MEhPXZz6RIXxfR5oxXs=
|
||||||
|
github.com/jackc/pgtype v1.8.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||||
|
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
||||||
|
github.com/jackc/pgx/v4 v4.13.0 h1:JCjhT5vmhMAf/YwBHLvrBn4OGdIQBiFG6ym8Zmdx570=
|
||||||
|
github.com/jackc/pgx/v4 v4.13.0/go.mod h1:9P4X524sErlaxj0XSGZk7s+LD0eOyu1ZDUrrpznYDF0=
|
||||||
|
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.1.3 h1:JnPg/5Q9xVJGfjsO5CPUOjnJps1JaRUm8I9FXVCFK94=
|
||||||
|
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jlaffaye/ftp v0.0.0-20211029032751-b1140299f4df h1:nsRFf9ZkcalB12ZJZYAD35TE2r+g/i088w9ytnaUvUo=
|
||||||
|
github.com/jlaffaye/ftp v0.0.0-20211029032751-b1140299f4df/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU=
|
||||||
|
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||||
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
|
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
|
github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||||
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
|
||||||
|
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||||
|
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
|
github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
|
||||||
|
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||||
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||||
|
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||||
|
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
|
||||||
|
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||||
|
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
|
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||||
|
github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
|
||||||
|
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
|
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
|
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
||||||
|
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
|
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||||
|
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||||
|
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||||
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
|
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||||
|
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
||||||
|
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||||
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/streadway/amqp v0.0.0-20181107104731-27835f1a64e9/go.mod h1:1WNBiOZtZQLpVAyu0iTduoJL9hEsMloAK5XWrtW0xdY=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
|
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||||
|
go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0=
|
||||||
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||||
|
go.opencensus.io v0.22.6/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||||
|
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||||
|
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||||
|
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||||
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
|
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||||
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
|
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||||
|
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||||
|
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||||
|
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||||
|
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||||
|
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||||
|
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||||
|
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||||
|
gocloud.dev v0.24.0 h1:cNtHD07zQQiv02OiwwDyVMuHmR7iQt2RLkzoAgz7wBs=
|
||||||
|
gocloud.dev v0.24.0/go.mod h1:uA+als++iBX5ShuG4upQo/3Zoz49iIPlYUWHV5mM8w8=
|
||||||
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
|
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||||
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
|
||||||
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||||
|
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||||
|
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||||
|
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||||
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||||
|
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
|
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw=
|
||||||
|
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210126194326-f9ce19ea3013/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210223095934-7937bea0104d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e h1:XMgFehsDnnLGtjvjOfqWSUzt0alpTR1RSEuznObga2c=
|
||||||
|
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
|
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
|
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||||
|
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||||
|
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
|
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
|
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
|
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||||
|
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||||
|
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||||
|
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||||
|
google.golang.org/api v0.37.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||||
|
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||||
|
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
|
||||||
|
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
|
||||||
|
google.golang.org/api v0.46.0/go.mod h1:ceL4oozhkAiTID8XMmJBsIxID/9wMXJVVFXPg4ylg3I=
|
||||||
|
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
|
||||||
|
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
|
||||||
|
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
|
||||||
|
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
|
||||||
|
google.golang.org/api v0.52.0/go.mod h1:Him/adpjt0sxtkWViy0b6xyKW/SD71CwdJ7HqJo7SrU=
|
||||||
|
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
|
||||||
|
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||||
|
google.golang.org/api v0.56.0 h1:08F9XVYTLOGeSQb3xI9C0gXMuQanhdGed0cWFhDozbI=
|
||||||
|
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||||
|
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||||
|
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||||
|
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||||
|
google.golang.org/genproto v0.0.0-20210429181445-86c259c2b4ab/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
||||||
|
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
||||||
|
google.golang.org/genproto v0.0.0-20210517163617-5e0236093d7a/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
||||||
|
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||||
|
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||||
|
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||||
|
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
|
||||||
|
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||||
|
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||||
|
google.golang.org/genproto v0.0.0-20210721163202-f1cecdd8b78a/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||||
|
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||||
|
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||||
|
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||||
|
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
|
||||||
|
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||||
|
google.golang.org/genproto v0.0.0-20210825212027-de86158e7fda/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||||
|
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||||
|
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2 h1:NHN4wOCScVzKhPenJ2dt+BTs3X/XkBVI/Rh4iDt55T8=
|
||||||
|
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
|
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||||
|
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||||
|
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||||
|
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
|
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||||
|
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
|
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
|
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
|
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||||
|
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||||
|
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||||
|
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||||
|
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||||
|
google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q=
|
||||||
|
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||||
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||||
|
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||||
|
gopkg.in/ini.v1 v1.39.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
gopkg.in/pipe.v2 v2.0.0-20140414041502-3c2ca4d52544/go.mod h1:UhTeH/yXCK/KY7TX24mqPkaQ7gZeqmWd/8SSS8B3aHw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||||
|
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||||
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
12
go/trains/webapi/default.nix
Normal file
12
go/trains/webapi/default.nix
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }:
|
||||||
|
depot.third_party.buildGo.package {
|
||||||
|
name = "webapi";
|
||||||
|
srcs = [
|
||||||
|
./structs.go
|
||||||
|
];
|
||||||
|
path = "hg.lukegb.com/lukegb/depot/go/trains/webapi";
|
||||||
|
}
|
197
go/trains/webapi/structs.go
Normal file
197
go/trains/webapi/structs.go
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
package webapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TrainOperator struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
|
||||||
|
Name *string `json:"name"`
|
||||||
|
URL *string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (toc *TrainOperator) IsEmpty() bool {
|
||||||
|
return toc == nil || toc.Code == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (toc *TrainOperator) Canonicalize() {
|
||||||
|
if toc == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if *toc.Name == "" {
|
||||||
|
toc.Name = nil
|
||||||
|
}
|
||||||
|
if *toc.URL == "" {
|
||||||
|
toc.URL = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Location struct {
|
||||||
|
TIPLOC string `json:"tiploc"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
CRS *string `json:"crs"`
|
||||||
|
TOC *TrainOperator `json:"operator"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (loc *Location) IsEmpty() bool {
|
||||||
|
return loc == nil || loc.TIPLOC == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (loc *Location) Canonicalize() {
|
||||||
|
if loc == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if loc.CRS == nil || *loc.CRS == "" {
|
||||||
|
loc.CRS = nil
|
||||||
|
}
|
||||||
|
if loc.TOC.IsEmpty() {
|
||||||
|
loc.TOC = nil
|
||||||
|
}
|
||||||
|
loc.TOC.Canonicalize()
|
||||||
|
}
|
||||||
|
|
||||||
|
type DisruptionReason struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
|
||||||
|
Location *Location `json:"location"`
|
||||||
|
NearLocation bool `json:"near_location"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dr *DisruptionReason) IsEmpty() bool {
|
||||||
|
return dr == nil || dr.Code == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dr *DisruptionReason) Canonicalize() {
|
||||||
|
if dr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if dr.Location.IsEmpty() {
|
||||||
|
dr.Location = nil
|
||||||
|
}
|
||||||
|
dr.Location.Canonicalize()
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServiceData struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
RID string `json:"rid"`
|
||||||
|
UID string `json:"uid"`
|
||||||
|
RSID string `json:"rsid"`
|
||||||
|
Headcode string `json:"headcode"`
|
||||||
|
StartDate string `json:"scheduled_start_date"`
|
||||||
|
|
||||||
|
TrainOperator TrainOperator `json:"operator"`
|
||||||
|
|
||||||
|
DelayReason *DisruptionReason `json:"delay_reason"`
|
||||||
|
CancelReason *DisruptionReason `json:"cancel_reason"`
|
||||||
|
|
||||||
|
Active bool `json:"is_active"`
|
||||||
|
Deleted bool `json:"is_deleted"`
|
||||||
|
Cancelled bool `json:"is_cancelled"`
|
||||||
|
|
||||||
|
Locations []ServiceLocation `json:"locations"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sd *ServiceData) Canonicalize() {
|
||||||
|
if sd.DelayReason.IsEmpty() {
|
||||||
|
sd.DelayReason = nil
|
||||||
|
}
|
||||||
|
sd.DelayReason.Canonicalize()
|
||||||
|
if sd.CancelReason.IsEmpty() {
|
||||||
|
sd.CancelReason = nil
|
||||||
|
}
|
||||||
|
sd.CancelReason.Canonicalize()
|
||||||
|
}
|
||||||
|
|
||||||
|
type TimingData struct {
|
||||||
|
PublicScheduled *time.Time `json:"public_scheduled"`
|
||||||
|
WorkingScheduled *time.Time `json:"working_scheduled"`
|
||||||
|
|
||||||
|
PublicEstimated *time.Time `json:"public_estimated"`
|
||||||
|
WorkingEstimated *time.Time `json:"working_estimated"`
|
||||||
|
|
||||||
|
Actual *time.Time `json:"actual"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (td *TimingData) IsEmpty() bool {
|
||||||
|
if td == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return td.PublicScheduled == nil && td.WorkingScheduled == nil && td.PublicEstimated == nil && td.WorkingEstimated == nil && td.Actual == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (td *TimingData) Canonicalize() {
|
||||||
|
if td == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ts := []**time.Time{&td.PublicScheduled, &td.WorkingScheduled, &td.PublicEstimated, &td.WorkingEstimated, &td.Actual}
|
||||||
|
for _, t := range ts {
|
||||||
|
if *t == nil || (*t).IsZero() {
|
||||||
|
*t = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlatformData struct {
|
||||||
|
Scheduled string `json:"scheduled"`
|
||||||
|
|
||||||
|
Live string `json:"live"`
|
||||||
|
Confirmed bool `json:"confirmed"`
|
||||||
|
|
||||||
|
Suppressed bool `json:"platform_suppressed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pd *PlatformData) IsEmpty() bool {
|
||||||
|
return pd.Scheduled == "" && pd.Live == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pd *PlatformData) Canonicalize() {}
|
||||||
|
|
||||||
|
type ServiceLocation struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Location *Location `json:"location"`
|
||||||
|
CallingPointType string `json:"calling_point_type"`
|
||||||
|
|
||||||
|
Length *int `json:"train_length"`
|
||||||
|
Suppressed bool `json:"service_suppressed"`
|
||||||
|
Platform *PlatformData `json:"platform"`
|
||||||
|
|
||||||
|
Cancelled bool `json:"cancelled"`
|
||||||
|
FalseDestination *Location `json:"false_destination"`
|
||||||
|
|
||||||
|
ArrivalTiming *TimingData `json:"arrival_timing"`
|
||||||
|
DepartureTiming *TimingData `json:"departure_timing"`
|
||||||
|
PassTiming *TimingData `json:"pass_timing"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sl *ServiceLocation) Canonicalize() {
|
||||||
|
if sl == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sl.Location.Canonicalize()
|
||||||
|
if sl.Length == nil || *sl.Length == 0 {
|
||||||
|
sl.Length = nil
|
||||||
|
}
|
||||||
|
if sl.Platform.IsEmpty() {
|
||||||
|
sl.Platform = nil
|
||||||
|
}
|
||||||
|
sl.Platform.Canonicalize()
|
||||||
|
if sl.FalseDestination.IsEmpty() {
|
||||||
|
sl.FalseDestination = nil
|
||||||
|
}
|
||||||
|
sl.FalseDestination.Canonicalize()
|
||||||
|
if sl.ArrivalTiming.IsEmpty() {
|
||||||
|
sl.ArrivalTiming = nil
|
||||||
|
}
|
||||||
|
sl.ArrivalTiming.Canonicalize()
|
||||||
|
if sl.DepartureTiming.IsEmpty() {
|
||||||
|
sl.DepartureTiming = nil
|
||||||
|
}
|
||||||
|
sl.DepartureTiming.Canonicalize()
|
||||||
|
if sl.PassTiming.IsEmpty() {
|
||||||
|
sl.PassTiming = nil
|
||||||
|
}
|
||||||
|
sl.PassTiming.Canonicalize()
|
||||||
|
}
|
14
third_party/gopkgs/github.com/cenkalti/backoff/v4/default.nix
vendored
Normal file
14
third_party/gopkgs/github.com/cenkalti/backoff/v4/default.nix
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }:
|
||||||
|
depot.third_party.buildGo.external {
|
||||||
|
path = "github.com/cenkalti/backoff/v4";
|
||||||
|
src = depot.third_party.nixpkgs.fetchFromGitHub {
|
||||||
|
owner = "cenkalti";
|
||||||
|
repo = "backoff";
|
||||||
|
rev = "v4.1.2";
|
||||||
|
hash = "sha256:08c28226q612i1pv83161y57qh16631vpc51ai9f76qfrzsy946z";
|
||||||
|
};
|
||||||
|
}
|
14
third_party/gopkgs/github.com/go-stomp/stomp/v3/default.nix
vendored
Normal file
14
third_party/gopkgs/github.com/go-stomp/stomp/v3/default.nix
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }:
|
||||||
|
depot.third_party.buildGo.external {
|
||||||
|
path = "github.com/go-stomp/stomp/v3";
|
||||||
|
src = depot.third_party.nixpkgs.fetchFromGitHub {
|
||||||
|
owner = "go-stomp";
|
||||||
|
repo = "stomp";
|
||||||
|
rev = "v3.0.3";
|
||||||
|
hash = "sha256:0plm1p5nibqaaqbs4qxaxc3vr9h5xwx5jp3z2fv3nip07slg7rb6";
|
||||||
|
};
|
||||||
|
}
|
14
third_party/gopkgs/github.com/jlaffaye/ftp/default.nix
vendored
Normal file
14
third_party/gopkgs/github.com/jlaffaye/ftp/default.nix
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }:
|
||||||
|
depot.third_party.buildGo.external {
|
||||||
|
path = "github.com/jlaffaye/ftp";
|
||||||
|
src = depot.third_party.nixpkgs.fetchFromGitHub {
|
||||||
|
owner = "jlaffaye";
|
||||||
|
repo = "ftp";
|
||||||
|
rev = "11820403398b118877718a169d6368edc6e3ef9a";
|
||||||
|
hash = "sha256:18v142q292wp5y8flnq15iyfb33801xz1sg4c9pn2aibi546j011";
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in a new issue