From 46fdd15f32799bb4f4c288bd6f5c193087cec3eb Mon Sep 17 00:00:00 2001 From: Alec Thomas Date: Wed, 30 Oct 2024 20:18:41 +1100 Subject: [PATCH] fix: correctly record event times (#3254) Fixes #3240 Before: image After: image --- Justfile | 1 + backend/controller/timeline/events_call.go | 8 ++-- backend/controller/timeline/events_cron.go | 11 +++-- backend/controller/timeline/events_ingress.go | 47 +++++++++++------- backend/controller/timeline/events_log.go | 4 +- backend/controller/timeline/timeline.go | 27 +++++++---- examples/go/time/go.mod | 45 +++++++++++++++++ examples/go/time/go.sum | 48 ++++++++++++++++++- examples/go/time/time.go | 8 +++- examples/go/time/types.ftl.go | 2 + 10 files changed, 159 insertions(+), 42 deletions(-) diff --git a/Justfile b/Justfile index 8cd1d982db..766d035dee 100644 --- a/Justfile +++ b/Justfile @@ -205,6 +205,7 @@ lint-frontend: build-frontend lint-backend: @golangci-lint run --new-from-rev=$(git merge-base origin/main HEAD) ./... @lint-commit-or-rollback ./backend/... + @go-check-sumtype ./... lint-scripts: #!/bin/bash diff --git a/backend/controller/timeline/events_call.go b/backend/controller/timeline/events_call.go index cc981dea5a..b470eea0b2 100644 --- a/backend/controller/timeline/events_call.go +++ b/backend/controller/timeline/events_call.go @@ -55,11 +55,9 @@ type Call struct { Response either.Either[*ftlv1.CallResponse, error] } -func (c *Call) inEvent() {} - -func (s *Service) insertCallEvent(ctx context.Context, querier sql.Querier, call *Call) error { - callEvent := callToCallEvent(call) +func (c *Call) toEvent() (Event, error) { return callToCallEvent(c), nil } //nolint:unparam +func (s *Service) insertCallEvent(ctx context.Context, querier sql.Querier, callEvent *CallEvent) error { var sourceModule, sourceVerb optional.Option[string] if sr, ok := callEvent.SourceVerb.Get(); ok { sourceModule, sourceVerb = optional.Some(sr.Module), optional.Some(sr.Name) @@ -95,7 +93,7 @@ func (s *Service) insertCallEvent(ctx context.Context, querier sql.Querier, call } err = libdal.TranslatePGError(querier.InsertTimelineCallEvent(ctx, sql.InsertTimelineCallEventParams{ - DeploymentKey: call.DeploymentKey, + DeploymentKey: callEvent.DeploymentKey, RequestKey: requestKey, ParentRequestKey: parentRequestKey, TimeStamp: callEvent.Time, diff --git a/backend/controller/timeline/events_cron.go b/backend/controller/timeline/events_cron.go index 6fba45be26..40394a3b2d 100644 --- a/backend/controller/timeline/events_cron.go +++ b/backend/controller/timeline/events_cron.go @@ -34,7 +34,12 @@ type CronScheduled struct { Error optional.Option[string] } -func (*CronScheduled) inEvent() {} +func (e *CronScheduled) toEvent() (Event, error) { //nolint:unparam + return &CronScheduledEvent{ + CronScheduled: *e, + Duration: time.Since(e.Time), + }, nil +} type eventCronScheduledJSON struct { DurationMS int64 `json:"duration_ms"` @@ -43,9 +48,9 @@ type eventCronScheduledJSON struct { Error optional.Option[string] `json:"error,omitempty"` } -func (s *Service) insertCronScheduledEvent(ctx context.Context, querier sql.Querier, event *CronScheduled) error { +func (s *Service) insertCronScheduledEvent(ctx context.Context, querier sql.Querier, event *CronScheduledEvent) error { cronJSON := eventCronScheduledJSON{ - DurationMS: time.Since(event.Time).Milliseconds(), + DurationMS: event.Duration.Milliseconds(), ScheduledAt: event.ScheduledAt, Schedule: event.Schedule, Error: event.Error, diff --git a/backend/controller/timeline/events_ingress.go b/backend/controller/timeline/events_ingress.go index b1ad91cc8c..c218f76247 100644 --- a/backend/controller/timeline/events_ingress.go +++ b/backend/controller/timeline/events_ingress.go @@ -65,9 +65,7 @@ type Ingress struct { Error optional.Option[string] } -func (*Ingress) inEvent() {} - -func (s *Service) insertHTTPIngress(ctx context.Context, querier sql.Querier, ingress *Ingress) error { +func (ingress *Ingress) toEvent() (Event, error) { requestBody := ingress.RequestBody if len(requestBody) == 0 { requestBody = []byte("{}") @@ -78,29 +76,42 @@ func (s *Service) insertHTTPIngress(ctx context.Context, querier sql.Querier, in responseBody = []byte("{}") } - if len(responseBody) == 0 { - responseBody = []byte("{}") - } - reqHeaderBytes, err := json.Marshal(ingress.RequestHeaders) if err != nil { - return fmt.Errorf("failed to marshal request header: %w", err) + return nil, fmt.Errorf("failed to marshal request header: %w", err) } respHeaderBytes, err := json.Marshal(ingress.ResponseHeaders) if err != nil { - return fmt.Errorf("failed to marshal response header: %w", err) + return nil, fmt.Errorf("failed to marshal response header: %w", err) } - - ingressJSON := eventIngressJSON{ - DurationMS: time.Since(ingress.StartTime).Milliseconds(), + return &IngressEvent{ + DeploymentKey: ingress.DeploymentKey, + RequestKey: optional.Some(ingress.RequestKey), + Verb: *ingress.Verb, Method: ingress.RequestMethod, Path: ingress.RequestPath, StatusCode: ingress.ResponseStatus, - Request: json.RawMessage(requestBody), - RequestHeader: json.RawMessage(reqHeaderBytes), - Response: json.RawMessage(responseBody), - ResponseHeader: json.RawMessage(respHeaderBytes), + Time: ingress.StartTime, + Duration: time.Since(ingress.StartTime), + Request: requestBody, + RequestHeader: reqHeaderBytes, + Response: responseBody, + ResponseHeader: respHeaderBytes, + Error: ingress.Error, + }, nil +} + +func (s *Service) insertHTTPIngress(ctx context.Context, querier sql.Querier, ingress *IngressEvent) error { + ingressJSON := eventIngressJSON{ + DurationMS: ingress.Duration.Milliseconds(), + Method: ingress.Method, + Path: ingress.Path, + StatusCode: ingress.StatusCode, + Request: ingress.Request, + RequestHeader: ingress.RequestHeader, + Response: ingress.Response, + ResponseHeader: ingress.ResponseHeader, Error: ingress.Error, } @@ -115,12 +126,12 @@ func (s *Service) insertHTTPIngress(ctx context.Context, querier sql.Querier, in return fmt.Errorf("failed to encrypt ingress payload: %w", err) } - log.FromContext(ctx).Warnf("Inserting ingress event for %s %s", ingress.RequestKey, ingress.RequestPath) + log.FromContext(ctx).Warnf("Inserting ingress event for %s %s", ingress.RequestKey, ingress.Path) err = libdal.TranslatePGError(querier.InsertTimelineIngressEvent(ctx, sql.InsertTimelineIngressEventParams{ DeploymentKey: ingress.DeploymentKey, RequestKey: optional.Some(ingress.RequestKey.String()), - TimeStamp: ingress.StartTime, + TimeStamp: ingress.Time, Module: ingress.Verb.Module, Verb: ingress.Verb.Name, IngressType: "http", diff --git a/backend/controller/timeline/events_log.go b/backend/controller/timeline/events_log.go index dcfd6d0048..67952382b1 100644 --- a/backend/controller/timeline/events_log.go +++ b/backend/controller/timeline/events_log.go @@ -24,7 +24,7 @@ type Log struct { Error optional.Option[string] } -func (l *Log) inEvent() {} +func (l *Log) toEvent() (Event, error) { return &LogEvent{Log: *l}, nil } //nolint:unparam type LogEvent struct { ID int64 @@ -40,7 +40,7 @@ type eventLogJSON struct { Error optional.Option[string] `json:"error,omitempty"` } -func (s *Service) insertLogEvent(ctx context.Context, querier sql.Querier, log *Log) error { +func (s *Service) insertLogEvent(ctx context.Context, querier sql.Querier, log *LogEvent) error { var requestKey optional.Option[string] if name, ok := log.RequestKey.Get(); ok { requestKey = optional.Some(name.String()) diff --git a/backend/controller/timeline/timeline.go b/backend/controller/timeline/timeline.go index d129a32058..cb3f8333b7 100644 --- a/backend/controller/timeline/timeline.go +++ b/backend/controller/timeline/timeline.go @@ -41,21 +41,21 @@ type Event interface { // InEvent is a marker interface for events that are inserted into the timeline. type InEvent interface { - inEvent() + toEvent() (Event, error) } type Service struct { ctx context.Context conn *stdsql.DB encryption *encryption.Service - events chan InEvent + events chan Event lastDroppedError atomic.Value[time.Time] lastFailedError atomic.Value[time.Time] } func New(ctx context.Context, conn *stdsql.DB, encryption *encryption.Service) *Service { var s *Service - events := make(chan InEvent, 1000) + events := make(chan Event, 1000) s = &Service{ ctx: ctx, conn: conn, @@ -72,7 +72,12 @@ func (s *Service) DeleteOldEvents(ctx context.Context, eventType EventType, age } // EnqueueEvent asynchronously enqueues an event for insertion into the timeline. -func (s *Service) EnqueueEvent(ctx context.Context, event InEvent) { +func (s *Service) EnqueueEvent(ctx context.Context, inEvent InEvent) { + event, err := inEvent.toEvent() + if err != nil { + log.FromContext(ctx).Warnf("Failed to convert event to event: %v", err) + return + } select { case s.events <- event: default: @@ -85,7 +90,7 @@ func (s *Service) EnqueueEvent(ctx context.Context, event InEvent) { func (s *Service) processEvents() { lastFlush := time.Now() - buffer := make([]InEvent, 0, maxBatchSize) + buffer := make([]Event, 0, maxBatchSize) for { select { case event := <-s.events: @@ -108,7 +113,7 @@ func (s *Service) processEvents() { } // Flush all events in the buffer to the database in a single transaction. -func (s *Service) flushEvents(events []InEvent) { +func (s *Service) flushEvents(events []Event) { logger := log.FromContext(s.ctx).Scope("timeline") tx, err := s.conn.Begin() if err != nil { @@ -121,14 +126,16 @@ func (s *Service) flushEvents(events []InEvent) { for _, event := range events { var err error switch e := event.(type) { - case *Call: + case *CallEvent: err = s.insertCallEvent(s.ctx, querier, e) - case *Log: + case *LogEvent: err = s.insertLogEvent(s.ctx, querier, e) - case *Ingress: + case *IngressEvent: err = s.insertHTTPIngress(s.ctx, querier, e) - case *CronScheduled: + case *CronScheduledEvent: err = s.insertCronScheduledEvent(s.ctx, querier, e) + case *DeploymentCreatedEvent, *DeploymentUpdatedEvent: + // TODO: Implement default: panic(fmt.Sprintf("unexpected event type: %T", e)) } diff --git a/examples/go/time/go.mod b/examples/go/time/go.mod index d6957b7e6a..e5f491975a 100644 --- a/examples/go/time/go.mod +++ b/examples/go/time/go.mod @@ -7,12 +7,57 @@ replace github.com/TBD54566975/ftl => ../../.. require github.com/TBD54566975/ftl v0.0.0-00010101000000-000000000000 require ( + al.essio.dev/pkg/shellescape v1.5.1 // indirect + connectrpc.com/connect v1.16.2 // indirect + connectrpc.com/grpcreflect v1.2.0 // indirect + connectrpc.com/otelconnect v0.7.1 // indirect + github.com/XSAM/otelsql v0.35.0 // indirect + github.com/alecthomas/atomic v0.1.0-alpha2 // indirect + github.com/alecthomas/concurrency v0.0.2 // indirect + github.com/alecthomas/kong v1.2.1 // indirect github.com/alecthomas/participle/v2 v2.1.1 // indirect github.com/alecthomas/types v0.16.0 // indirect + github.com/benbjohnson/clock v1.3.5 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/danieljoos/wincred v1.2.2 // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/hashicorp/cronexpr v1.1.2 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.1 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/jpillora/backoff v1.0.0 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/puzpuzpuz/xsync/v3 v3.4.0 // indirect github.com/swaggest/jsonschema-go v0.3.72 // indirect github.com/swaggest/refl v1.3.0 // indirect + github.com/zalando/go-keyring v0.2.6 // indirect + go.opentelemetry.io/otel v1.31.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 // indirect + go.opentelemetry.io/otel/metric v1.31.0 // indirect + go.opentelemetry.io/otel/sdk v1.31.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.31.0 // indirect + go.opentelemetry.io/otel/trace v1.31.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect + golang.org/x/crypto v0.28.0 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect + golang.org/x/mod v0.21.0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect + google.golang.org/grpc v1.67.1 // indirect google.golang.org/protobuf v1.35.1 // indirect ) diff --git a/examples/go/time/go.sum b/examples/go/time/go.sum index c74b6955ee..eff813097d 100644 --- a/examples/go/time/go.sum +++ b/examples/go/time/go.sum @@ -8,10 +8,16 @@ connectrpc.com/otelconnect v0.7.1 h1:scO5pOb0i4yUE66CnNrHeK1x51yq0bE0ehPg6WvzXJY connectrpc.com/otelconnect v0.7.1/go.mod h1:dh3bFgHBTb2bkqGCeVVOtHJreSns7uu9wwL2Tbz17ms= github.com/TBD54566975/scaffolder v1.2.0 h1:7NqCC+iTDgk2awkOgk2Pj95Nz3GonhdIhwHQgQxug9k= github.com/TBD54566975/scaffolder v1.2.0/go.mod h1:oHLiKFPkkSMHP4ALVZ91T2V/xyx4MvPpRSpQttJxY7g= +github.com/XSAM/otelsql v0.35.0 h1:nMdbU/XLmBIB6qZF61uDqy46E0LVA4ZgF/FCNw8Had4= +github.com/XSAM/otelsql v0.35.0/go.mod h1:wO028mnLzmBpstK8XPsoeRLl/kgt417yjAwOGDIptTc= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/atomic v0.1.0-alpha2 h1:dqwXmax66gXvHhsOS4pGPZKqYOlTkapELkLb3MNdlH8= +github.com/alecthomas/atomic v0.1.0-alpha2/go.mod h1:zD6QGEyw49HIq19caJDc2NMXAy8rNi9ROrxtMXATfyI= github.com/alecthomas/concurrency v0.0.2 h1:Q3kGPtLbleMbH9lHX5OBFvJygfyFw29bXZKBg+IEVuo= github.com/alecthomas/concurrency v0.0.2/go.mod h1:GmuQb/iHX7mbNtPlC/WDzEFxDMB0HYFer2Qda9QTs7w= +github.com/alecthomas/kong v1.2.1 h1:E8jH4Tsgv6wCRX2nGrdPyHDUCSG83WH2qE4XLACD33Q= +github.com/alecthomas/kong v1.2.1/go.mod h1:rKTSFhbdp3Ryefn8x5MOEprnRFQ7nlmMC01GKhehhBM= github.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6icjJvbsmV8= github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= @@ -24,8 +30,11 @@ github.com/bool64/dev v0.2.35 h1:M17TLsO/pV2J7PYI/gpe3Ua26ETkzZGb+dC06eoMqlk= github.com/bool64/dev v0.2.35/go.mod h1:iJbh1y/HkunEPhgebWRNcs8wfGq7sjvJ6W5iabL8ACg= github.com/bool64/shared v0.1.5 h1:fp3eUhBsrSjNCQPcSdQqZxxh9bBwrYiZ+zOKFkM0/2E= github.com/bool64/shared v0.1.5/go.mod h1:081yz68YC9jeFB3+Bbmno2RFWvGKv1lPKkMP6MHJlPs= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0= github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= @@ -36,6 +45,7 @@ github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxER github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -58,8 +68,12 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -104,6 +118,8 @@ github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= 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/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/puzpuzpuz/xsync/v3 v3.4.0 h1:DuVBAdXuGFHv8adVXjWWZ63pJq+NRXOWVXlKDBZ+mJ4= github.com/puzpuzpuz/xsync/v3 v3.4.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= @@ -114,8 +130,13 @@ github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7o2xQ= github.com/swaggest/assertjson v1.9.0/go.mod h1:b+ZKX2VRiUjxfUIal0HDN85W0nHPAYUbYH5WkkSsFsU= github.com/swaggest/jsonschema-go v0.3.72 h1:IHaGlR1bdBUBPfhe4tfacN2TGAPKENEGiNyNzvnVHv4= @@ -132,10 +153,26 @@ github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8u github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI= go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 h1:FZ6ei8GFW7kyPYdxJaV2rgI6M+4tvZzhYsQ2wgyVC08= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0/go.mod h1:MdEu/mC6j3D+tTEfvI15b5Ci2Fn7NneJ71YMoiS3tpI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy03iVTXP6ffeN2iXrxfGsZGCjVx0/4KlizjyBwU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0/go.mod h1:TMu73/k1CP8nBUpDLc71Wj/Kf7ZS9FK5b53VapRsP9o= go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= +go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= +go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= @@ -148,6 +185,7 @@ golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= @@ -158,12 +196,18 @@ golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg= google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= diff --git a/examples/go/time/time.go b/examples/go/time/time.go index f2d3cc1975..804ab008af 100644 --- a/examples/go/time/time.go +++ b/examples/go/time/time.go @@ -13,8 +13,12 @@ type TimeResponse struct { // Time returns the current time. // //ftl:verb export -func Time(ctx context.Context, req TimeRequest) (TimeResponse, error) { - return TimeResponse{Time: time.Now()}, nil +func Time(ctx context.Context, req TimeRequest, ic InternalClient) (TimeResponse, error) { + internalTime, err := ic(ctx, req) + if err != nil { + return TimeResponse{}, err + } + return TimeResponse{Time: internalTime.Time}, nil } //ftl:verb diff --git a/examples/go/time/types.ftl.go b/examples/go/time/types.ftl.go index 3d617c2c94..136efa8c0f 100644 --- a/examples/go/time/types.ftl.go +++ b/examples/go/time/types.ftl.go @@ -4,6 +4,7 @@ package time import ( "context" "github.com/TBD54566975/ftl/go-runtime/ftl/reflection" + "github.com/TBD54566975/ftl/go-runtime/server" ) type InternalClient func(context.Context, TimeRequest) (TimeResponse, error) @@ -17,6 +18,7 @@ func init() { ), reflection.ProvideResourcesForVerb( Time, + server.VerbClient[InternalClient, TimeRequest, TimeResponse](), ), ) }