diff --git a/backend/controller/console/console.go b/backend/controller/console/console.go index 1b046f2010..f649bf9900 100644 --- a/backend/controller/console/console.go +++ b/backend/controller/console/console.go @@ -16,8 +16,8 @@ import ( timelinepb "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/timeline/v1" "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/timeline/v1/timelinev1connect" ftlv1 "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/v1" + "github.com/TBD54566975/ftl/backend/timeline" "github.com/TBD54566975/ftl/internal/buildengine" - "github.com/TBD54566975/ftl/internal/rpc" "github.com/TBD54566975/ftl/internal/schema" "github.com/TBD54566975/ftl/internal/slices" ) @@ -549,7 +549,7 @@ func buildGraph(sch *schema.Schema, module *schema.Module, out map[string][]stri } func (c *ConsoleService) GetTimeline(ctx context.Context, req *connect.Request[timelinepb.GetTimelineRequest]) (*connect.Response[timelinepb.GetTimelineResponse], error) { - client := rpc.ClientFromContext[timelinev1connect.TimelineServiceClient](ctx) + client := timeline.ClientFromContext(ctx) resp, err := client.GetTimeline(ctx, connect.NewRequest(req.Msg)) if err != nil { return nil, fmt.Errorf("failed to get timeline from service: %w", err) @@ -558,7 +558,7 @@ func (c *ConsoleService) GetTimeline(ctx context.Context, req *connect.Request[t } func (c *ConsoleService) StreamTimeline(ctx context.Context, req *connect.Request[timelinepb.StreamTimelineRequest], out *connect.ServerStream[timelinepb.StreamTimelineResponse]) error { - client := rpc.ClientFromContext[timelinev1connect.TimelineServiceClient](ctx) + client := timeline.ClientFromContext(ctx) stream, err := client.StreamTimeline(ctx, req) if err != nil { return fmt.Errorf("failed to stream timeline from service: %w", err) @@ -576,7 +576,7 @@ func (c *ConsoleService) StreamTimeline(ctx context.Context, req *connect.Reques } return nil } -func (c *ConsoleService) CreateEvent(ctx context.Context, req *connect.Request[timelinepb.CreateEventRequest]) (*connect.Response[timelinepb.CreateEventResponse], error) { +func (c *ConsoleService) CreateEvents(ctx context.Context, req *connect.Request[timelinepb.CreateEventsRequest]) (*connect.Response[timelinepb.CreateEventsResponse], error) { return nil, fmt.Errorf("not implemented") } diff --git a/backend/controller/controller.go b/backend/controller/controller.go index 75420a8bc9..2b7ce5b9b2 100644 --- a/backend/controller/controller.go +++ b/backend/controller/controller.go @@ -423,7 +423,7 @@ func (s *Service) StreamDeploymentLogs(ctx context.Context, stream *connect.Clie requestKey = optional.Some(rkey) } - timeline.Publish(ctx, timeline.Log{ + timeline.ClientFromContext(ctx).Publish(ctx, timeline.Log{ DeploymentKey: deploymentKey, RequestKey: requestKey, Time: msg.TimeStamp.AsTime(), @@ -828,7 +828,7 @@ func (s *Service) PublishEvent(ctx context.Context, req *connect.Request[ftldepl routes := s.routeTable.Current() route, ok := routes.GetDeployment(module).Get() if ok { - timeline.Publish(ctx, timeline.PubSubPublish{ + timeline.ClientFromContext(ctx).Publish(ctx, timeline.PubSubPublish{ DeploymentKey: route, RequestKey: requestKey, Time: now, @@ -942,7 +942,7 @@ func (s *Service) callWithRequest( observability.Calls.Request(ctx, req.Msg.Verb, start, optional.Some("invalid request: verb not exported")) err = connect.NewError(connect.CodePermissionDenied, fmt.Errorf("verb %q is not exported", verbRef)) callEvent.Response = result.Err[*ftlv1.CallResponse](err) - timeline.Publish(ctx, callEvent) + timeline.ClientFromContext(ctx).Publish(ctx, callEvent) return nil, connect.NewError(connect.CodePermissionDenied, fmt.Errorf("verb %q is not exported", verbRef)) } @@ -950,7 +950,7 @@ func (s *Service) callWithRequest( if err != nil { observability.Calls.Request(ctx, req.Msg.Verb, start, optional.Some("invalid request: invalid call body")) callEvent.Response = result.Err[*ftlv1.CallResponse](err) - timeline.Publish(ctx, callEvent) + timeline.ClientFromContext(ctx).Publish(ctx, callEvent) return nil, err } @@ -975,7 +975,7 @@ func (s *Service) callWithRequest( logger.Errorf(err, "Call failed to verb %s for module %s", verbRef.String(), module) } - timeline.Publish(ctx, callEvent) + timeline.ClientFromContext(ctx).Publish(ctx, callEvent) return resp, err } @@ -1187,7 +1187,7 @@ func (s *Service) executeAsyncCalls(ctx context.Context) (interval time.Duration if e, ok := err.Get(); ok { errStr = optional.Some(e.Error()) } - timeline.Publish(ctx, timeline.AsyncExecute{ + timeline.ClientFromContext(ctx).Publish(ctx, timeline.AsyncExecute{ DeploymentKey: deployment, RequestKey: call.ParentRequestKey, EventType: eventType, @@ -1609,7 +1609,7 @@ func (s *Service) reapCallEvents(ctx context.Context) (time.Duration, error) { return time.Hour, nil } - client := rpc.ClientFromContext[timelinev1connect.TimelineServiceClient](ctx) + client := timeline.ClientFromContext(ctx) resp, err := client.DeleteOldEvents(ctx, connect.NewRequest(&timelinepb.DeleteOldEventsRequest{ EventType: timelinepb.EventType_EVENT_TYPE_CALL, AgeSeconds: int64(s.config.EventLogRetention.Seconds()), diff --git a/backend/controller/dal/dal.go b/backend/controller/dal/dal.go index 8e77a1175d..68e81b2316 100644 --- a/backend/controller/dal/dal.go +++ b/backend/controller/dal/dal.go @@ -212,7 +212,7 @@ func (d *DAL) SetDeploymentReplicas(ctx context.Context, key model.DeploymentKey return libdal.TranslatePGError(err) } } - timeline.Publish(ctx, timeline.DeploymentUpdated{ + timeline.ClientFromContext(ctx).Publish(ctx, timeline.DeploymentUpdated{ DeploymentKey: key, MinReplicas: minReplicas, PrevMinReplicas: int(deployment.MinReplicas), @@ -272,7 +272,7 @@ func (d *DAL) ReplaceDeployment(ctx context.Context, newDeploymentKey model.Depl } } - timeline.Publish(ctx, timeline.DeploymentCreated{ + timeline.ClientFromContext(ctx).Publish(ctx, timeline.DeploymentCreated{ DeploymentKey: newDeploymentKey, Language: newDeployment.Language, ModuleName: newDeployment.ModuleName, diff --git a/backend/controller/dal/dal_test.go b/backend/controller/dal/dal_test.go index 7d17ff30f5..446d0cb63d 100644 --- a/backend/controller/dal/dal_test.go +++ b/backend/controller/dal/dal_test.go @@ -3,7 +3,7 @@ package dal import ( "bytes" "context" - "net/http" + "net/url" "sync" "testing" "time" @@ -13,7 +13,7 @@ import ( "golang.org/x/sync/errgroup" "github.com/TBD54566975/ftl/backend/controller/artefacts" - "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/timeline/v1/timelinev1connect" + "github.com/TBD54566975/ftl/backend/timeline" dalmodel "github.com/TBD54566975/ftl/backend/controller/dal/model" "github.com/TBD54566975/ftl/backend/controller/pubsub" @@ -21,15 +21,15 @@ import ( "github.com/TBD54566975/ftl/backend/libdal" "github.com/TBD54566975/ftl/internal/log" "github.com/TBD54566975/ftl/internal/model" - "github.com/TBD54566975/ftl/internal/rpc" "github.com/TBD54566975/ftl/internal/schema" "github.com/TBD54566975/ftl/internal/sha256" ) func TestDAL(t *testing.T) { ctx := log.ContextWithNewDefaultLogger(context.Background()) - timelineClient := timelinev1connect.NewTimelineServiceClient(http.DefaultClient, "http://localhost:8080") - ctx = rpc.ContextWithClient(ctx, timelineClient) + timelineEndpoint, err := url.Parse("http://localhost:8080") + assert.NoError(t, err) + ctx = timeline.ContextWithClient(ctx, timeline.NewClient(ctx, timelineEndpoint)) conn := sqltest.OpenForTesting(ctx, t) pubSub := pubsub.New(ctx, conn, optional.None[pubsub.AsyncCallListener]()) @@ -38,7 +38,6 @@ func TestDAL(t *testing.T) { var testContent = bytes.Repeat([]byte("sometestcontentthatislongerthanthereadbuffer"), 100) var testSHA = sha256.Sum(testContent) - var err error deploymentChangesCh := dal.DeploymentChanges.Subscribe(nil) deploymentChanges := []DeploymentNotification{} wg := errgroup.Group{} diff --git a/backend/controller/deployment_logs.go b/backend/controller/deployment_logs.go index d79a8b532e..4f1a69705e 100644 --- a/backend/controller/deployment_logs.go +++ b/backend/controller/deployment_logs.go @@ -69,7 +69,7 @@ func (d *deploymentLogsSink) processLogs(ctx context.Context) { errorStr = optional.Some(entry.Error.Error()) } - timeline.Publish(ctx, &timeline.Log{ + timeline.ClientFromContext(ctx).Publish(ctx, &timeline.Log{ RequestKey: request, DeploymentKey: deployment, Time: entry.Time, diff --git a/backend/controller/pubsub/internal/dal/dal.go b/backend/controller/pubsub/internal/dal/dal.go index c507faba4e..37362c89dd 100644 --- a/backend/controller/pubsub/internal/dal/dal.go +++ b/backend/controller/pubsub/internal/dal/dal.go @@ -108,7 +108,7 @@ func (d *DAL) ProgressSubscriptions(ctx context.Context, eventConsumptionDelay t for _, subscription := range subs { now := time.Now().UTC() enqueueTimelineEvent := func(destVerb optional.Option[schema.RefKey], err optional.Option[string]) { - timeline.Publish(ctx, &timeline.PubSubConsume{ + timeline.ClientFromContext(ctx).Publish(ctx, &timeline.PubSubConsume{ DeploymentKey: subscription.DeploymentKey, RequestKey: subscription.RequestKey, Time: now, diff --git a/backend/cron/service.go b/backend/cron/service.go index 719aca774c..b45b073f69 100644 --- a/backend/cron/service.go +++ b/backend/cron/service.go @@ -132,7 +132,7 @@ func scheduleNext(ctx context.Context, cronQueue []cronJob) (time.Duration, bool if len(cronQueue) == 0 { return 0, false } - timeline.Publish(ctx, timeline.CronScheduled{ + timeline.ClientFromContext(ctx).Publish(ctx, timeline.CronScheduled{ DeploymentKey: model.NewDeploymentKey(cronQueue[0].module), Verb: schema.Ref{Module: cronQueue[0].module, Name: cronQueue[0].verb.Name}, ScheduledAt: cronQueue[0].next, diff --git a/backend/cron/service_test.go b/backend/cron/service_test.go index 672056e646..77363cc89a 100644 --- a/backend/cron/service_test.go +++ b/backend/cron/service_test.go @@ -2,7 +2,7 @@ package cron import ( "context" - "net/http" + "net/url" "os" "sort" "testing" @@ -15,12 +15,11 @@ import ( "github.com/alecthomas/types/optional" schemapb "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/schema/v1" - "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/timeline/v1/timelinev1connect" ftlv1 "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/v1" + "github.com/TBD54566975/ftl/backend/timeline" "github.com/TBD54566975/ftl/internal/log" "github.com/TBD54566975/ftl/internal/model" "github.com/TBD54566975/ftl/internal/routing" - "github.com/TBD54566975/ftl/internal/rpc" "github.com/TBD54566975/ftl/internal/schema" "github.com/TBD54566975/ftl/internal/schema/schemaeventsource" ) @@ -65,7 +64,9 @@ func TestCron(t *testing.T) { }) ctx := log.ContextWithLogger(context.Background(), log.Configure(os.Stderr, log.Config{Level: log.Trace})) - ctx = rpc.ContextWithClient(ctx, timelinev1connect.NewTimelineServiceClient(http.DefaultClient, "http://localhost:8080")) + timelineEndpoint, err := url.Parse("http://localhost:8080") + assert.NoError(t, err) + ctx = timeline.ContextWithClient(ctx, timeline.NewClient(ctx, timelineEndpoint)) ctx, cancel := context.WithTimeout(ctx, time.Second*5) t.Cleanup(cancel) @@ -112,6 +113,6 @@ done: }, }, requests, assert.Exclude[*schemapb.Position]()) - err := wg.Wait() + err = wg.Wait() assert.IsError(t, err, context.Canceled) } diff --git a/backend/ingress/handler.go b/backend/ingress/handler.go index b7c90ecc8e..cef8a616f2 100644 --- a/backend/ingress/handler.go +++ b/backend/ingress/handler.go @@ -144,7 +144,7 @@ func handleHTTP(startTime time.Time, sch *schema.Schema, requestKey model.Reques _, err = w.Write(responseBody) if err == nil { observability.Ingress.Request(r.Context(), r.Method, r.URL.Path, optional.Some(verbRef), startTime, optional.None[string]()) - timeline.Publish(r.Context(), ingressEvent) + timeline.ClientFromContext(r.Context()).Publish(r.Context(), ingressEvent) } else { logger.Errorf(err, "could not write response body") observability.Ingress.Request(r.Context(), r.Method, r.URL.Path, optional.Some(verbRef), startTime, optional.Some("could not write response body")) @@ -166,7 +166,7 @@ func recordIngressErrorEvent( ) { ingressEvent.ResponseStatus = statusCode ingressEvent.Error = optional.Some(errorMsg) - timeline.Publish(ctx, ingressEvent) + timeline.ClientFromContext(ctx).Publish(ctx, ingressEvent) } // Copied from the Apache-licensed connect-go source. diff --git a/backend/ingress/handler_test.go b/backend/ingress/handler_test.go index 351519cebc..a5e276b81c 100644 --- a/backend/ingress/handler_test.go +++ b/backend/ingress/handler_test.go @@ -13,12 +13,11 @@ import ( "github.com/alecthomas/assert/v2" "github.com/alecthomas/types/optional" - "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/timeline/v1/timelinev1connect" ftlv1 "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/v1" + "github.com/TBD54566975/ftl/backend/timeline" "github.com/TBD54566975/ftl/go-runtime/encoding" "github.com/TBD54566975/ftl/internal/log" "github.com/TBD54566975/ftl/internal/model" - "github.com/TBD54566975/ftl/internal/rpc" "github.com/TBD54566975/ftl/internal/schema" ) @@ -68,7 +67,9 @@ func TestIngress(t *testing.T) { } ctx := log.ContextWithNewDefaultLogger(context.Background()) - ctx = rpc.ContextWithClient(ctx, timelinev1connect.NewTimelineServiceClient(http.DefaultClient, "http://localhost:8080")) + timelineEndpoint, err := url.Parse("http://localhost:8080") + assert.NoError(t, err) + ctx = timeline.ContextWithClient(ctx, timeline.NewClient(ctx, timelineEndpoint)) assert.NoError(t, err) for _, test := range []struct { diff --git a/backend/protos/xyz/block/ftl/timeline/v1/timeline.pb.go b/backend/protos/xyz/block/ftl/timeline/v1/timeline.pb.go index c8bfcf63c2..19a5e50d2f 100644 --- a/backend/protos/xyz/block/ftl/timeline/v1/timeline.pb.go +++ b/backend/protos/xyz/block/ftl/timeline/v1/timeline.pb.go @@ -79,7 +79,9 @@ type GetTimelineRequest struct { Filters []*GetTimelineRequest_Filter `protobuf:"bytes,1,rep,name=filters,proto3" json:"filters,omitempty"` Limit int32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` - Order GetTimelineRequest_Order `protobuf:"varint,3,opt,name=order,proto3,enum=xyz.block.ftl.timeline.v1.GetTimelineRequest_Order" json:"order,omitempty"` + // Ordering is done by id which matches publication order. + // This roughly corresponds to the time of the event, but not strictly. + Order GetTimelineRequest_Order `protobuf:"varint,3,opt,name=order,proto3,enum=xyz.block.ftl.timeline.v1.GetTimelineRequest_Order" json:"order,omitempty"` } func (x *GetTimelineRequest) Reset() { @@ -285,39 +287,28 @@ func (x *StreamTimelineResponse) GetEvents() []*Event { return nil } -type CreateEventRequest struct { +type CreateEventsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Types that are assignable to Entry: - // - // *CreateEventRequest_Log - // *CreateEventRequest_Call - // *CreateEventRequest_DeploymentCreated - // *CreateEventRequest_DeploymentUpdated - // *CreateEventRequest_Ingress - // *CreateEventRequest_CronScheduled - // *CreateEventRequest_AsyncExecute - // *CreateEventRequest_PubsubPublish - // *CreateEventRequest_PubsubConsume - Entry isCreateEventRequest_Entry `protobuf_oneof:"entry"` -} - -func (x *CreateEventRequest) Reset() { - *x = CreateEventRequest{} + Entries []*CreateEventsRequest_EventEntry `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"` +} + +func (x *CreateEventsRequest) Reset() { + *x = CreateEventsRequest{} mi := &file_xyz_block_ftl_timeline_v1_timeline_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *CreateEventRequest) String() string { +func (x *CreateEventsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CreateEventRequest) ProtoMessage() {} +func (*CreateEventsRequest) ProtoMessage() {} -func (x *CreateEventRequest) ProtoReflect() protoreflect.Message { +func (x *CreateEventsRequest) ProtoReflect() protoreflect.Message { mi := &file_xyz_block_ftl_timeline_v1_timeline_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -329,159 +320,38 @@ func (x *CreateEventRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CreateEventRequest.ProtoReflect.Descriptor instead. -func (*CreateEventRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use CreateEventsRequest.ProtoReflect.Descriptor instead. +func (*CreateEventsRequest) Descriptor() ([]byte, []int) { return file_xyz_block_ftl_timeline_v1_timeline_proto_rawDescGZIP(), []int{4} } -func (m *CreateEventRequest) GetEntry() isCreateEventRequest_Entry { - if m != nil { - return m.Entry - } - return nil -} - -func (x *CreateEventRequest) GetLog() *LogEvent { - if x, ok := x.GetEntry().(*CreateEventRequest_Log); ok { - return x.Log - } - return nil -} - -func (x *CreateEventRequest) GetCall() *CallEvent { - if x, ok := x.GetEntry().(*CreateEventRequest_Call); ok { - return x.Call - } - return nil -} - -func (x *CreateEventRequest) GetDeploymentCreated() *DeploymentCreatedEvent { - if x, ok := x.GetEntry().(*CreateEventRequest_DeploymentCreated); ok { - return x.DeploymentCreated - } - return nil -} - -func (x *CreateEventRequest) GetDeploymentUpdated() *DeploymentUpdatedEvent { - if x, ok := x.GetEntry().(*CreateEventRequest_DeploymentUpdated); ok { - return x.DeploymentUpdated - } - return nil -} - -func (x *CreateEventRequest) GetIngress() *IngressEvent { - if x, ok := x.GetEntry().(*CreateEventRequest_Ingress); ok { - return x.Ingress - } - return nil -} - -func (x *CreateEventRequest) GetCronScheduled() *CronScheduledEvent { - if x, ok := x.GetEntry().(*CreateEventRequest_CronScheduled); ok { - return x.CronScheduled - } - return nil -} - -func (x *CreateEventRequest) GetAsyncExecute() *AsyncExecuteEvent { - if x, ok := x.GetEntry().(*CreateEventRequest_AsyncExecute); ok { - return x.AsyncExecute - } - return nil -} - -func (x *CreateEventRequest) GetPubsubPublish() *PubSubPublishEvent { - if x, ok := x.GetEntry().(*CreateEventRequest_PubsubPublish); ok { - return x.PubsubPublish - } - return nil -} - -func (x *CreateEventRequest) GetPubsubConsume() *PubSubConsumeEvent { - if x, ok := x.GetEntry().(*CreateEventRequest_PubsubConsume); ok { - return x.PubsubConsume +func (x *CreateEventsRequest) GetEntries() []*CreateEventsRequest_EventEntry { + if x != nil { + return x.Entries } return nil } -type isCreateEventRequest_Entry interface { - isCreateEventRequest_Entry() -} - -type CreateEventRequest_Log struct { - Log *LogEvent `protobuf:"bytes,1,opt,name=log,proto3,oneof"` -} - -type CreateEventRequest_Call struct { - Call *CallEvent `protobuf:"bytes,2,opt,name=call,proto3,oneof"` -} - -type CreateEventRequest_DeploymentCreated struct { - DeploymentCreated *DeploymentCreatedEvent `protobuf:"bytes,3,opt,name=deployment_created,json=deploymentCreated,proto3,oneof"` -} - -type CreateEventRequest_DeploymentUpdated struct { - DeploymentUpdated *DeploymentUpdatedEvent `protobuf:"bytes,4,opt,name=deployment_updated,json=deploymentUpdated,proto3,oneof"` -} - -type CreateEventRequest_Ingress struct { - Ingress *IngressEvent `protobuf:"bytes,5,opt,name=ingress,proto3,oneof"` -} - -type CreateEventRequest_CronScheduled struct { - CronScheduled *CronScheduledEvent `protobuf:"bytes,6,opt,name=cron_scheduled,json=cronScheduled,proto3,oneof"` -} - -type CreateEventRequest_AsyncExecute struct { - AsyncExecute *AsyncExecuteEvent `protobuf:"bytes,7,opt,name=async_execute,json=asyncExecute,proto3,oneof"` -} - -type CreateEventRequest_PubsubPublish struct { - PubsubPublish *PubSubPublishEvent `protobuf:"bytes,8,opt,name=pubsub_publish,json=pubsubPublish,proto3,oneof"` -} - -type CreateEventRequest_PubsubConsume struct { - PubsubConsume *PubSubConsumeEvent `protobuf:"bytes,9,opt,name=pubsub_consume,json=pubsubConsume,proto3,oneof"` -} - -func (*CreateEventRequest_Log) isCreateEventRequest_Entry() {} - -func (*CreateEventRequest_Call) isCreateEventRequest_Entry() {} - -func (*CreateEventRequest_DeploymentCreated) isCreateEventRequest_Entry() {} - -func (*CreateEventRequest_DeploymentUpdated) isCreateEventRequest_Entry() {} - -func (*CreateEventRequest_Ingress) isCreateEventRequest_Entry() {} - -func (*CreateEventRequest_CronScheduled) isCreateEventRequest_Entry() {} - -func (*CreateEventRequest_AsyncExecute) isCreateEventRequest_Entry() {} - -func (*CreateEventRequest_PubsubPublish) isCreateEventRequest_Entry() {} - -func (*CreateEventRequest_PubsubConsume) isCreateEventRequest_Entry() {} - -type CreateEventResponse struct { +type CreateEventsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } -func (x *CreateEventResponse) Reset() { - *x = CreateEventResponse{} +func (x *CreateEventsResponse) Reset() { + *x = CreateEventsResponse{} mi := &file_xyz_block_ftl_timeline_v1_timeline_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *CreateEventResponse) String() string { +func (x *CreateEventsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CreateEventResponse) ProtoMessage() {} +func (*CreateEventsResponse) ProtoMessage() {} -func (x *CreateEventResponse) ProtoReflect() protoreflect.Message { +func (x *CreateEventsResponse) ProtoReflect() protoreflect.Message { mi := &file_xyz_block_ftl_timeline_v1_timeline_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -493,8 +363,8 @@ func (x *CreateEventResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CreateEventResponse.ProtoReflect.Descriptor instead. -func (*CreateEventResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use CreateEventsResponse.ProtoReflect.Descriptor instead. +func (*CreateEventsResponse) Descriptor() ([]byte, []int) { return file_xyz_block_ftl_timeline_v1_timeline_proto_rawDescGZIP(), []int{5} } @@ -1172,6 +1042,191 @@ func (*GetTimelineRequest_Filter_Call) isGetTimelineRequest_Filter_Filter() {} func (*GetTimelineRequest_Filter_Module) isGetTimelineRequest_Filter_Filter() {} +type CreateEventsRequest_EventEntry struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // Types that are assignable to Entry: + // + // *CreateEventsRequest_EventEntry_Log + // *CreateEventsRequest_EventEntry_Call + // *CreateEventsRequest_EventEntry_DeploymentCreated + // *CreateEventsRequest_EventEntry_DeploymentUpdated + // *CreateEventsRequest_EventEntry_Ingress + // *CreateEventsRequest_EventEntry_CronScheduled + // *CreateEventsRequest_EventEntry_AsyncExecute + // *CreateEventsRequest_EventEntry_PubsubPublish + // *CreateEventsRequest_EventEntry_PubsubConsume + Entry isCreateEventsRequest_EventEntry_Entry `protobuf_oneof:"entry"` +} + +func (x *CreateEventsRequest_EventEntry) Reset() { + *x = CreateEventsRequest_EventEntry{} + mi := &file_xyz_block_ftl_timeline_v1_timeline_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateEventsRequest_EventEntry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateEventsRequest_EventEntry) ProtoMessage() {} + +func (x *CreateEventsRequest_EventEntry) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_timeline_v1_timeline_proto_msgTypes[17] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateEventsRequest_EventEntry.ProtoReflect.Descriptor instead. +func (*CreateEventsRequest_EventEntry) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_timeline_v1_timeline_proto_rawDescGZIP(), []int{4, 0} +} + +func (x *CreateEventsRequest_EventEntry) GetTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.Timestamp + } + return nil +} + +func (m *CreateEventsRequest_EventEntry) GetEntry() isCreateEventsRequest_EventEntry_Entry { + if m != nil { + return m.Entry + } + return nil +} + +func (x *CreateEventsRequest_EventEntry) GetLog() *LogEvent { + if x, ok := x.GetEntry().(*CreateEventsRequest_EventEntry_Log); ok { + return x.Log + } + return nil +} + +func (x *CreateEventsRequest_EventEntry) GetCall() *CallEvent { + if x, ok := x.GetEntry().(*CreateEventsRequest_EventEntry_Call); ok { + return x.Call + } + return nil +} + +func (x *CreateEventsRequest_EventEntry) GetDeploymentCreated() *DeploymentCreatedEvent { + if x, ok := x.GetEntry().(*CreateEventsRequest_EventEntry_DeploymentCreated); ok { + return x.DeploymentCreated + } + return nil +} + +func (x *CreateEventsRequest_EventEntry) GetDeploymentUpdated() *DeploymentUpdatedEvent { + if x, ok := x.GetEntry().(*CreateEventsRequest_EventEntry_DeploymentUpdated); ok { + return x.DeploymentUpdated + } + return nil +} + +func (x *CreateEventsRequest_EventEntry) GetIngress() *IngressEvent { + if x, ok := x.GetEntry().(*CreateEventsRequest_EventEntry_Ingress); ok { + return x.Ingress + } + return nil +} + +func (x *CreateEventsRequest_EventEntry) GetCronScheduled() *CronScheduledEvent { + if x, ok := x.GetEntry().(*CreateEventsRequest_EventEntry_CronScheduled); ok { + return x.CronScheduled + } + return nil +} + +func (x *CreateEventsRequest_EventEntry) GetAsyncExecute() *AsyncExecuteEvent { + if x, ok := x.GetEntry().(*CreateEventsRequest_EventEntry_AsyncExecute); ok { + return x.AsyncExecute + } + return nil +} + +func (x *CreateEventsRequest_EventEntry) GetPubsubPublish() *PubSubPublishEvent { + if x, ok := x.GetEntry().(*CreateEventsRequest_EventEntry_PubsubPublish); ok { + return x.PubsubPublish + } + return nil +} + +func (x *CreateEventsRequest_EventEntry) GetPubsubConsume() *PubSubConsumeEvent { + if x, ok := x.GetEntry().(*CreateEventsRequest_EventEntry_PubsubConsume); ok { + return x.PubsubConsume + } + return nil +} + +type isCreateEventsRequest_EventEntry_Entry interface { + isCreateEventsRequest_EventEntry_Entry() +} + +type CreateEventsRequest_EventEntry_Log struct { + Log *LogEvent `protobuf:"bytes,2,opt,name=log,proto3,oneof"` +} + +type CreateEventsRequest_EventEntry_Call struct { + Call *CallEvent `protobuf:"bytes,3,opt,name=call,proto3,oneof"` +} + +type CreateEventsRequest_EventEntry_DeploymentCreated struct { + DeploymentCreated *DeploymentCreatedEvent `protobuf:"bytes,4,opt,name=deployment_created,json=deploymentCreated,proto3,oneof"` +} + +type CreateEventsRequest_EventEntry_DeploymentUpdated struct { + DeploymentUpdated *DeploymentUpdatedEvent `protobuf:"bytes,5,opt,name=deployment_updated,json=deploymentUpdated,proto3,oneof"` +} + +type CreateEventsRequest_EventEntry_Ingress struct { + Ingress *IngressEvent `protobuf:"bytes,6,opt,name=ingress,proto3,oneof"` +} + +type CreateEventsRequest_EventEntry_CronScheduled struct { + CronScheduled *CronScheduledEvent `protobuf:"bytes,7,opt,name=cron_scheduled,json=cronScheduled,proto3,oneof"` +} + +type CreateEventsRequest_EventEntry_AsyncExecute struct { + AsyncExecute *AsyncExecuteEvent `protobuf:"bytes,8,opt,name=async_execute,json=asyncExecute,proto3,oneof"` +} + +type CreateEventsRequest_EventEntry_PubsubPublish struct { + PubsubPublish *PubSubPublishEvent `protobuf:"bytes,9,opt,name=pubsub_publish,json=pubsubPublish,proto3,oneof"` +} + +type CreateEventsRequest_EventEntry_PubsubConsume struct { + PubsubConsume *PubSubConsumeEvent `protobuf:"bytes,10,opt,name=pubsub_consume,json=pubsubConsume,proto3,oneof"` +} + +func (*CreateEventsRequest_EventEntry_Log) isCreateEventsRequest_EventEntry_Entry() {} + +func (*CreateEventsRequest_EventEntry_Call) isCreateEventsRequest_EventEntry_Entry() {} + +func (*CreateEventsRequest_EventEntry_DeploymentCreated) isCreateEventsRequest_EventEntry_Entry() {} + +func (*CreateEventsRequest_EventEntry_DeploymentUpdated) isCreateEventsRequest_EventEntry_Entry() {} + +func (*CreateEventsRequest_EventEntry_Ingress) isCreateEventsRequest_EventEntry_Entry() {} + +func (*CreateEventsRequest_EventEntry_CronScheduled) isCreateEventsRequest_EventEntry_Entry() {} + +func (*CreateEventsRequest_EventEntry_AsyncExecute) isCreateEventsRequest_EventEntry_Entry() {} + +func (*CreateEventsRequest_EventEntry_PubsubPublish) isCreateEventsRequest_EventEntry_Entry() {} + +func (*CreateEventsRequest_EventEntry_PubsubConsume) isCreateEventsRequest_EventEntry_Entry() {} + var File_xyz_block_ftl_timeline_v1_timeline_proto protoreflect.FileDescriptor var file_xyz_block_ftl_timeline_v1_timeline_proto_rawDesc = []byte{ @@ -1323,56 +1378,66 @@ var file_xyz_block_ftl_timeline_v1_timeline_proto_rawDesc = []byte{ 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xfc, 0x05, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x37, 0x0a, 0x03, - 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x79, 0x7a, 0x2e, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x9b, 0x07, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x53, 0x0a, + 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x39, + 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x74, + 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, + 0x65, 0x73, 0x1a, 0xae, 0x06, 0x0a, 0x0a, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x37, 0x0a, 0x03, 0x6c, + 0x6f, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, + 0x03, 0x6c, 0x6f, 0x67, 0x12, 0x3a, 0x0a, 0x04, 0x63, 0x61, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, + 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x61, 0x6c, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x04, 0x63, 0x61, 0x6c, 0x6c, + 0x12, 0x62, 0x0a, 0x12, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x78, + 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, + 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, + 0x00, 0x52, 0x11, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x12, 0x62, 0x0a, 0x12, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x31, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, + 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, + 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x11, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x43, 0x0a, 0x07, 0x69, 0x6e, 0x67, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, - 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, - 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x12, 0x3a, 0x0a, 0x04, 0x63, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, - 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x61, 0x6c, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x04, 0x63, 0x61, 0x6c, - 0x6c, 0x12, 0x62, 0x0a, 0x12, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, - 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, - 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, - 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x48, 0x00, 0x52, 0x11, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x62, 0x0a, 0x12, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x31, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, - 0x6c, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, - 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x11, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x43, 0x0a, 0x07, 0x69, 0x6e, 0x67, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, + 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x56, 0x0a, + 0x0e, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x72, 0x6f, 0x6e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x63, 0x72, 0x6f, 0x6e, 0x53, 0x63, 0x68, 0x65, + 0x64, 0x75, 0x6c, 0x65, 0x64, 0x12, 0x53, 0x0a, 0x0d, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x65, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, + 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, + 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x45, 0x78, + 0x65, 0x63, 0x75, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x61, 0x73, + 0x79, 0x6e, 0x63, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x56, 0x0a, 0x0e, 0x70, 0x75, + 0x62, 0x73, 0x75, 0x62, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, + 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, + 0x75, 0x62, 0x53, 0x75, 0x62, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x48, 0x00, 0x52, 0x0d, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x50, 0x75, 0x62, 0x6c, 0x69, + 0x73, 0x68, 0x12, 0x56, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x5f, 0x63, 0x6f, 0x6e, + 0x73, 0x75, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, - 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x56, - 0x0a, 0x0e, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x72, 0x6f, 0x6e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x63, 0x72, 0x6f, 0x6e, 0x53, 0x63, 0x68, - 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x12, 0x53, 0x0a, 0x0d, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x5f, - 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, - 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, - 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x45, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x61, - 0x73, 0x79, 0x6e, 0x63, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x56, 0x0a, 0x0e, 0x70, - 0x75, 0x62, 0x73, 0x75, 0x62, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, - 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x50, 0x75, 0x62, 0x6c, - 0x69, 0x73, 0x68, 0x12, 0x56, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x5f, 0x63, 0x6f, - 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x79, - 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, 0x65, - 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x43, 0x6f, - 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x70, 0x75, - 0x62, 0x73, 0x75, 0x62, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x65, - 0x6e, 0x74, 0x72, 0x79, 0x22, 0x15, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7e, 0x0a, 0x16, 0x44, + 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x43, 0x6f, 0x6e, + 0x73, 0x75, 0x6d, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x70, 0x75, 0x62, + 0x73, 0x75, 0x62, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x6e, + 0x74, 0x72, 0x79, 0x22, 0x16, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7e, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x6c, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, @@ -1384,7 +1449,7 @@ var file_xyz_block_ftl_timeline_v1_timeline_proto_rawDesc = []byte{ 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x6c, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x64, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x32, 0xb5, 0x04, 0x0a, 0x0f, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x32, 0xb8, 0x04, 0x0a, 0x0f, 0x54, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x1d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, @@ -1405,27 +1470,28 @@ var file_xyz_block_ftl_timeline_v1_timeline_proto_rawDesc = []byte{ 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x6e, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7a, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x4f, 0x6c, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x31, 0x2e, 0x78, 0x79, 0x7a, - 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, - 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x6c, 0x64, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x71, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7a, 0x0a, 0x0f, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x6c, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x31, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x4f, 0x6c, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x42, 0x52, 0x50, 0x01, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x42, 0x44, 0x35, 0x34, 0x35, 0x36, 0x36, 0x39, 0x37, 0x35, 0x2f, - 0x66, 0x74, 0x6c, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x73, 0x2f, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, - 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x74, 0x69, 0x6d, - 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x4f, 0x6c, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x32, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, + 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x4f, 0x6c, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x52, 0x50, 0x01, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x42, 0x44, 0x35, 0x34, 0x35, 0x36, 0x36, 0x39, + 0x37, 0x35, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, + 0x66, 0x74, 0x6c, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, + 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -1441,15 +1507,15 @@ func file_xyz_block_ftl_timeline_v1_timeline_proto_rawDescGZIP() []byte { } var file_xyz_block_ftl_timeline_v1_timeline_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_xyz_block_ftl_timeline_v1_timeline_proto_msgTypes = make([]protoimpl.MessageInfo, 17) +var file_xyz_block_ftl_timeline_v1_timeline_proto_msgTypes = make([]protoimpl.MessageInfo, 18) var file_xyz_block_ftl_timeline_v1_timeline_proto_goTypes = []any{ (GetTimelineRequest_Order)(0), // 0: xyz.block.ftl.timeline.v1.GetTimelineRequest.Order (*GetTimelineRequest)(nil), // 1: xyz.block.ftl.timeline.v1.GetTimelineRequest (*GetTimelineResponse)(nil), // 2: xyz.block.ftl.timeline.v1.GetTimelineResponse (*StreamTimelineRequest)(nil), // 3: xyz.block.ftl.timeline.v1.StreamTimelineRequest (*StreamTimelineResponse)(nil), // 4: xyz.block.ftl.timeline.v1.StreamTimelineResponse - (*CreateEventRequest)(nil), // 5: xyz.block.ftl.timeline.v1.CreateEventRequest - (*CreateEventResponse)(nil), // 6: xyz.block.ftl.timeline.v1.CreateEventResponse + (*CreateEventsRequest)(nil), // 5: xyz.block.ftl.timeline.v1.CreateEventsRequest + (*CreateEventsResponse)(nil), // 6: xyz.block.ftl.timeline.v1.CreateEventsResponse (*DeleteOldEventsRequest)(nil), // 7: xyz.block.ftl.timeline.v1.DeleteOldEventsRequest (*DeleteOldEventsResponse)(nil), // 8: xyz.block.ftl.timeline.v1.DeleteOldEventsResponse (*GetTimelineRequest_LogLevelFilter)(nil), // 9: xyz.block.ftl.timeline.v1.GetTimelineRequest.LogLevelFilter @@ -1461,67 +1527,70 @@ var file_xyz_block_ftl_timeline_v1_timeline_proto_goTypes = []any{ (*GetTimelineRequest_CallFilter)(nil), // 15: xyz.block.ftl.timeline.v1.GetTimelineRequest.CallFilter (*GetTimelineRequest_ModuleFilter)(nil), // 16: xyz.block.ftl.timeline.v1.GetTimelineRequest.ModuleFilter (*GetTimelineRequest_Filter)(nil), // 17: xyz.block.ftl.timeline.v1.GetTimelineRequest.Filter - (*Event)(nil), // 18: xyz.block.ftl.timeline.v1.Event - (*durationpb.Duration)(nil), // 19: google.protobuf.Duration - (*LogEvent)(nil), // 20: xyz.block.ftl.timeline.v1.LogEvent - (*CallEvent)(nil), // 21: xyz.block.ftl.timeline.v1.CallEvent - (*DeploymentCreatedEvent)(nil), // 22: xyz.block.ftl.timeline.v1.DeploymentCreatedEvent - (*DeploymentUpdatedEvent)(nil), // 23: xyz.block.ftl.timeline.v1.DeploymentUpdatedEvent - (*IngressEvent)(nil), // 24: xyz.block.ftl.timeline.v1.IngressEvent - (*CronScheduledEvent)(nil), // 25: xyz.block.ftl.timeline.v1.CronScheduledEvent - (*AsyncExecuteEvent)(nil), // 26: xyz.block.ftl.timeline.v1.AsyncExecuteEvent - (*PubSubPublishEvent)(nil), // 27: xyz.block.ftl.timeline.v1.PubSubPublishEvent - (*PubSubConsumeEvent)(nil), // 28: xyz.block.ftl.timeline.v1.PubSubConsumeEvent - (EventType)(0), // 29: xyz.block.ftl.timeline.v1.EventType - (LogLevel)(0), // 30: xyz.block.ftl.timeline.v1.LogLevel - (*timestamppb.Timestamp)(nil), // 31: google.protobuf.Timestamp - (*v1.PingRequest)(nil), // 32: xyz.block.ftl.v1.PingRequest - (*v1.PingResponse)(nil), // 33: xyz.block.ftl.v1.PingResponse + (*CreateEventsRequest_EventEntry)(nil), // 18: xyz.block.ftl.timeline.v1.CreateEventsRequest.EventEntry + (*Event)(nil), // 19: xyz.block.ftl.timeline.v1.Event + (*durationpb.Duration)(nil), // 20: google.protobuf.Duration + (EventType)(0), // 21: xyz.block.ftl.timeline.v1.EventType + (LogLevel)(0), // 22: xyz.block.ftl.timeline.v1.LogLevel + (*timestamppb.Timestamp)(nil), // 23: google.protobuf.Timestamp + (*LogEvent)(nil), // 24: xyz.block.ftl.timeline.v1.LogEvent + (*CallEvent)(nil), // 25: xyz.block.ftl.timeline.v1.CallEvent + (*DeploymentCreatedEvent)(nil), // 26: xyz.block.ftl.timeline.v1.DeploymentCreatedEvent + (*DeploymentUpdatedEvent)(nil), // 27: xyz.block.ftl.timeline.v1.DeploymentUpdatedEvent + (*IngressEvent)(nil), // 28: xyz.block.ftl.timeline.v1.IngressEvent + (*CronScheduledEvent)(nil), // 29: xyz.block.ftl.timeline.v1.CronScheduledEvent + (*AsyncExecuteEvent)(nil), // 30: xyz.block.ftl.timeline.v1.AsyncExecuteEvent + (*PubSubPublishEvent)(nil), // 31: xyz.block.ftl.timeline.v1.PubSubPublishEvent + (*PubSubConsumeEvent)(nil), // 32: xyz.block.ftl.timeline.v1.PubSubConsumeEvent + (*v1.PingRequest)(nil), // 33: xyz.block.ftl.v1.PingRequest + (*v1.PingResponse)(nil), // 34: xyz.block.ftl.v1.PingResponse } var file_xyz_block_ftl_timeline_v1_timeline_proto_depIdxs = []int32{ 17, // 0: xyz.block.ftl.timeline.v1.GetTimelineRequest.filters:type_name -> xyz.block.ftl.timeline.v1.GetTimelineRequest.Filter 0, // 1: xyz.block.ftl.timeline.v1.GetTimelineRequest.order:type_name -> xyz.block.ftl.timeline.v1.GetTimelineRequest.Order - 18, // 2: xyz.block.ftl.timeline.v1.GetTimelineResponse.events:type_name -> xyz.block.ftl.timeline.v1.Event - 19, // 3: xyz.block.ftl.timeline.v1.StreamTimelineRequest.update_interval:type_name -> google.protobuf.Duration + 19, // 2: xyz.block.ftl.timeline.v1.GetTimelineResponse.events:type_name -> xyz.block.ftl.timeline.v1.Event + 20, // 3: xyz.block.ftl.timeline.v1.StreamTimelineRequest.update_interval:type_name -> google.protobuf.Duration 1, // 4: xyz.block.ftl.timeline.v1.StreamTimelineRequest.query:type_name -> xyz.block.ftl.timeline.v1.GetTimelineRequest - 18, // 5: xyz.block.ftl.timeline.v1.StreamTimelineResponse.events:type_name -> xyz.block.ftl.timeline.v1.Event - 20, // 6: xyz.block.ftl.timeline.v1.CreateEventRequest.log:type_name -> xyz.block.ftl.timeline.v1.LogEvent - 21, // 7: xyz.block.ftl.timeline.v1.CreateEventRequest.call:type_name -> xyz.block.ftl.timeline.v1.CallEvent - 22, // 8: xyz.block.ftl.timeline.v1.CreateEventRequest.deployment_created:type_name -> xyz.block.ftl.timeline.v1.DeploymentCreatedEvent - 23, // 9: xyz.block.ftl.timeline.v1.CreateEventRequest.deployment_updated:type_name -> xyz.block.ftl.timeline.v1.DeploymentUpdatedEvent - 24, // 10: xyz.block.ftl.timeline.v1.CreateEventRequest.ingress:type_name -> xyz.block.ftl.timeline.v1.IngressEvent - 25, // 11: xyz.block.ftl.timeline.v1.CreateEventRequest.cron_scheduled:type_name -> xyz.block.ftl.timeline.v1.CronScheduledEvent - 26, // 12: xyz.block.ftl.timeline.v1.CreateEventRequest.async_execute:type_name -> xyz.block.ftl.timeline.v1.AsyncExecuteEvent - 27, // 13: xyz.block.ftl.timeline.v1.CreateEventRequest.pubsub_publish:type_name -> xyz.block.ftl.timeline.v1.PubSubPublishEvent - 28, // 14: xyz.block.ftl.timeline.v1.CreateEventRequest.pubsub_consume:type_name -> xyz.block.ftl.timeline.v1.PubSubConsumeEvent - 29, // 15: xyz.block.ftl.timeline.v1.DeleteOldEventsRequest.event_type:type_name -> xyz.block.ftl.timeline.v1.EventType - 30, // 16: xyz.block.ftl.timeline.v1.GetTimelineRequest.LogLevelFilter.log_level:type_name -> xyz.block.ftl.timeline.v1.LogLevel - 29, // 17: xyz.block.ftl.timeline.v1.GetTimelineRequest.EventTypeFilter.event_types:type_name -> xyz.block.ftl.timeline.v1.EventType - 31, // 18: xyz.block.ftl.timeline.v1.GetTimelineRequest.TimeFilter.older_than:type_name -> google.protobuf.Timestamp - 31, // 19: xyz.block.ftl.timeline.v1.GetTimelineRequest.TimeFilter.newer_than:type_name -> google.protobuf.Timestamp - 9, // 20: xyz.block.ftl.timeline.v1.GetTimelineRequest.Filter.log_level:type_name -> xyz.block.ftl.timeline.v1.GetTimelineRequest.LogLevelFilter - 10, // 21: xyz.block.ftl.timeline.v1.GetTimelineRequest.Filter.deployments:type_name -> xyz.block.ftl.timeline.v1.GetTimelineRequest.DeploymentFilter - 11, // 22: xyz.block.ftl.timeline.v1.GetTimelineRequest.Filter.requests:type_name -> xyz.block.ftl.timeline.v1.GetTimelineRequest.RequestFilter - 12, // 23: xyz.block.ftl.timeline.v1.GetTimelineRequest.Filter.event_types:type_name -> xyz.block.ftl.timeline.v1.GetTimelineRequest.EventTypeFilter - 13, // 24: xyz.block.ftl.timeline.v1.GetTimelineRequest.Filter.time:type_name -> xyz.block.ftl.timeline.v1.GetTimelineRequest.TimeFilter - 14, // 25: xyz.block.ftl.timeline.v1.GetTimelineRequest.Filter.id:type_name -> xyz.block.ftl.timeline.v1.GetTimelineRequest.IDFilter - 15, // 26: xyz.block.ftl.timeline.v1.GetTimelineRequest.Filter.call:type_name -> xyz.block.ftl.timeline.v1.GetTimelineRequest.CallFilter - 16, // 27: xyz.block.ftl.timeline.v1.GetTimelineRequest.Filter.module:type_name -> xyz.block.ftl.timeline.v1.GetTimelineRequest.ModuleFilter - 32, // 28: xyz.block.ftl.timeline.v1.TimelineService.Ping:input_type -> xyz.block.ftl.v1.PingRequest - 1, // 29: xyz.block.ftl.timeline.v1.TimelineService.GetTimeline:input_type -> xyz.block.ftl.timeline.v1.GetTimelineRequest - 3, // 30: xyz.block.ftl.timeline.v1.TimelineService.StreamTimeline:input_type -> xyz.block.ftl.timeline.v1.StreamTimelineRequest - 5, // 31: xyz.block.ftl.timeline.v1.TimelineService.CreateEvent:input_type -> xyz.block.ftl.timeline.v1.CreateEventRequest - 7, // 32: xyz.block.ftl.timeline.v1.TimelineService.DeleteOldEvents:input_type -> xyz.block.ftl.timeline.v1.DeleteOldEventsRequest - 33, // 33: xyz.block.ftl.timeline.v1.TimelineService.Ping:output_type -> xyz.block.ftl.v1.PingResponse - 2, // 34: xyz.block.ftl.timeline.v1.TimelineService.GetTimeline:output_type -> xyz.block.ftl.timeline.v1.GetTimelineResponse - 4, // 35: xyz.block.ftl.timeline.v1.TimelineService.StreamTimeline:output_type -> xyz.block.ftl.timeline.v1.StreamTimelineResponse - 6, // 36: xyz.block.ftl.timeline.v1.TimelineService.CreateEvent:output_type -> xyz.block.ftl.timeline.v1.CreateEventResponse - 8, // 37: xyz.block.ftl.timeline.v1.TimelineService.DeleteOldEvents:output_type -> xyz.block.ftl.timeline.v1.DeleteOldEventsResponse - 33, // [33:38] is the sub-list for method output_type - 28, // [28:33] is the sub-list for method input_type - 28, // [28:28] is the sub-list for extension type_name - 28, // [28:28] is the sub-list for extension extendee - 0, // [0:28] is the sub-list for field type_name + 19, // 5: xyz.block.ftl.timeline.v1.StreamTimelineResponse.events:type_name -> xyz.block.ftl.timeline.v1.Event + 18, // 6: xyz.block.ftl.timeline.v1.CreateEventsRequest.entries:type_name -> xyz.block.ftl.timeline.v1.CreateEventsRequest.EventEntry + 21, // 7: xyz.block.ftl.timeline.v1.DeleteOldEventsRequest.event_type:type_name -> xyz.block.ftl.timeline.v1.EventType + 22, // 8: xyz.block.ftl.timeline.v1.GetTimelineRequest.LogLevelFilter.log_level:type_name -> xyz.block.ftl.timeline.v1.LogLevel + 21, // 9: xyz.block.ftl.timeline.v1.GetTimelineRequest.EventTypeFilter.event_types:type_name -> xyz.block.ftl.timeline.v1.EventType + 23, // 10: xyz.block.ftl.timeline.v1.GetTimelineRequest.TimeFilter.older_than:type_name -> google.protobuf.Timestamp + 23, // 11: xyz.block.ftl.timeline.v1.GetTimelineRequest.TimeFilter.newer_than:type_name -> google.protobuf.Timestamp + 9, // 12: xyz.block.ftl.timeline.v1.GetTimelineRequest.Filter.log_level:type_name -> xyz.block.ftl.timeline.v1.GetTimelineRequest.LogLevelFilter + 10, // 13: xyz.block.ftl.timeline.v1.GetTimelineRequest.Filter.deployments:type_name -> xyz.block.ftl.timeline.v1.GetTimelineRequest.DeploymentFilter + 11, // 14: xyz.block.ftl.timeline.v1.GetTimelineRequest.Filter.requests:type_name -> xyz.block.ftl.timeline.v1.GetTimelineRequest.RequestFilter + 12, // 15: xyz.block.ftl.timeline.v1.GetTimelineRequest.Filter.event_types:type_name -> xyz.block.ftl.timeline.v1.GetTimelineRequest.EventTypeFilter + 13, // 16: xyz.block.ftl.timeline.v1.GetTimelineRequest.Filter.time:type_name -> xyz.block.ftl.timeline.v1.GetTimelineRequest.TimeFilter + 14, // 17: xyz.block.ftl.timeline.v1.GetTimelineRequest.Filter.id:type_name -> xyz.block.ftl.timeline.v1.GetTimelineRequest.IDFilter + 15, // 18: xyz.block.ftl.timeline.v1.GetTimelineRequest.Filter.call:type_name -> xyz.block.ftl.timeline.v1.GetTimelineRequest.CallFilter + 16, // 19: xyz.block.ftl.timeline.v1.GetTimelineRequest.Filter.module:type_name -> xyz.block.ftl.timeline.v1.GetTimelineRequest.ModuleFilter + 23, // 20: xyz.block.ftl.timeline.v1.CreateEventsRequest.EventEntry.timestamp:type_name -> google.protobuf.Timestamp + 24, // 21: xyz.block.ftl.timeline.v1.CreateEventsRequest.EventEntry.log:type_name -> xyz.block.ftl.timeline.v1.LogEvent + 25, // 22: xyz.block.ftl.timeline.v1.CreateEventsRequest.EventEntry.call:type_name -> xyz.block.ftl.timeline.v1.CallEvent + 26, // 23: xyz.block.ftl.timeline.v1.CreateEventsRequest.EventEntry.deployment_created:type_name -> xyz.block.ftl.timeline.v1.DeploymentCreatedEvent + 27, // 24: xyz.block.ftl.timeline.v1.CreateEventsRequest.EventEntry.deployment_updated:type_name -> xyz.block.ftl.timeline.v1.DeploymentUpdatedEvent + 28, // 25: xyz.block.ftl.timeline.v1.CreateEventsRequest.EventEntry.ingress:type_name -> xyz.block.ftl.timeline.v1.IngressEvent + 29, // 26: xyz.block.ftl.timeline.v1.CreateEventsRequest.EventEntry.cron_scheduled:type_name -> xyz.block.ftl.timeline.v1.CronScheduledEvent + 30, // 27: xyz.block.ftl.timeline.v1.CreateEventsRequest.EventEntry.async_execute:type_name -> xyz.block.ftl.timeline.v1.AsyncExecuteEvent + 31, // 28: xyz.block.ftl.timeline.v1.CreateEventsRequest.EventEntry.pubsub_publish:type_name -> xyz.block.ftl.timeline.v1.PubSubPublishEvent + 32, // 29: xyz.block.ftl.timeline.v1.CreateEventsRequest.EventEntry.pubsub_consume:type_name -> xyz.block.ftl.timeline.v1.PubSubConsumeEvent + 33, // 30: xyz.block.ftl.timeline.v1.TimelineService.Ping:input_type -> xyz.block.ftl.v1.PingRequest + 1, // 31: xyz.block.ftl.timeline.v1.TimelineService.GetTimeline:input_type -> xyz.block.ftl.timeline.v1.GetTimelineRequest + 3, // 32: xyz.block.ftl.timeline.v1.TimelineService.StreamTimeline:input_type -> xyz.block.ftl.timeline.v1.StreamTimelineRequest + 5, // 33: xyz.block.ftl.timeline.v1.TimelineService.CreateEvents:input_type -> xyz.block.ftl.timeline.v1.CreateEventsRequest + 7, // 34: xyz.block.ftl.timeline.v1.TimelineService.DeleteOldEvents:input_type -> xyz.block.ftl.timeline.v1.DeleteOldEventsRequest + 34, // 35: xyz.block.ftl.timeline.v1.TimelineService.Ping:output_type -> xyz.block.ftl.v1.PingResponse + 2, // 36: xyz.block.ftl.timeline.v1.TimelineService.GetTimeline:output_type -> xyz.block.ftl.timeline.v1.GetTimelineResponse + 4, // 37: xyz.block.ftl.timeline.v1.TimelineService.StreamTimeline:output_type -> xyz.block.ftl.timeline.v1.StreamTimelineResponse + 6, // 38: xyz.block.ftl.timeline.v1.TimelineService.CreateEvents:output_type -> xyz.block.ftl.timeline.v1.CreateEventsResponse + 8, // 39: xyz.block.ftl.timeline.v1.TimelineService.DeleteOldEvents:output_type -> xyz.block.ftl.timeline.v1.DeleteOldEventsResponse + 35, // [35:40] is the sub-list for method output_type + 30, // [30:35] is the sub-list for method input_type + 30, // [30:30] is the sub-list for extension type_name + 30, // [30:30] is the sub-list for extension extendee + 0, // [0:30] is the sub-list for field type_name } func init() { file_xyz_block_ftl_timeline_v1_timeline_proto_init() } @@ -1532,17 +1601,6 @@ func file_xyz_block_ftl_timeline_v1_timeline_proto_init() { file_xyz_block_ftl_timeline_v1_event_proto_init() file_xyz_block_ftl_timeline_v1_timeline_proto_msgTypes[1].OneofWrappers = []any{} file_xyz_block_ftl_timeline_v1_timeline_proto_msgTypes[2].OneofWrappers = []any{} - file_xyz_block_ftl_timeline_v1_timeline_proto_msgTypes[4].OneofWrappers = []any{ - (*CreateEventRequest_Log)(nil), - (*CreateEventRequest_Call)(nil), - (*CreateEventRequest_DeploymentCreated)(nil), - (*CreateEventRequest_DeploymentUpdated)(nil), - (*CreateEventRequest_Ingress)(nil), - (*CreateEventRequest_CronScheduled)(nil), - (*CreateEventRequest_AsyncExecute)(nil), - (*CreateEventRequest_PubsubPublish)(nil), - (*CreateEventRequest_PubsubConsume)(nil), - } file_xyz_block_ftl_timeline_v1_timeline_proto_msgTypes[12].OneofWrappers = []any{} file_xyz_block_ftl_timeline_v1_timeline_proto_msgTypes[13].OneofWrappers = []any{} file_xyz_block_ftl_timeline_v1_timeline_proto_msgTypes[14].OneofWrappers = []any{} @@ -1557,13 +1615,24 @@ func file_xyz_block_ftl_timeline_v1_timeline_proto_init() { (*GetTimelineRequest_Filter_Call)(nil), (*GetTimelineRequest_Filter_Module)(nil), } + file_xyz_block_ftl_timeline_v1_timeline_proto_msgTypes[17].OneofWrappers = []any{ + (*CreateEventsRequest_EventEntry_Log)(nil), + (*CreateEventsRequest_EventEntry_Call)(nil), + (*CreateEventsRequest_EventEntry_DeploymentCreated)(nil), + (*CreateEventsRequest_EventEntry_DeploymentUpdated)(nil), + (*CreateEventsRequest_EventEntry_Ingress)(nil), + (*CreateEventsRequest_EventEntry_CronScheduled)(nil), + (*CreateEventsRequest_EventEntry_AsyncExecute)(nil), + (*CreateEventsRequest_EventEntry_PubsubPublish)(nil), + (*CreateEventsRequest_EventEntry_PubsubConsume)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_xyz_block_ftl_timeline_v1_timeline_proto_rawDesc, NumEnums: 1, - NumMessages: 17, + NumMessages: 18, NumExtensions: 0, NumServices: 1, }, diff --git a/backend/protos/xyz/block/ftl/timeline/v1/timeline.proto b/backend/protos/xyz/block/ftl/timeline/v1/timeline.proto index 9da8a86a03..010c590911 100644 --- a/backend/protos/xyz/block/ftl/timeline/v1/timeline.proto +++ b/backend/protos/xyz/block/ftl/timeline/v1/timeline.proto @@ -74,6 +74,8 @@ message GetTimelineRequest { repeated Filter filters = 1; int32 limit = 2; + // Ordering is done by id which matches publication order. + // This roughly corresponds to the time of the event, but not strictly. Order order = 3; } @@ -92,21 +94,27 @@ message StreamTimelineResponse { repeated timeline.v1.Event events = 1; } -message CreateEventRequest { - oneof entry { - LogEvent log = 1; - CallEvent call = 2; - DeploymentCreatedEvent deployment_created = 3; - DeploymentUpdatedEvent deployment_updated = 4; - IngressEvent ingress = 5; - CronScheduledEvent cron_scheduled = 6; - AsyncExecuteEvent async_execute = 7; - PubSubPublishEvent pubsub_publish = 8; - PubSubConsumeEvent pubsub_consume = 9; +message CreateEventsRequest { + message EventEntry { + google.protobuf.Timestamp timestamp = 1; + + oneof entry { + LogEvent log = 2; + CallEvent call = 3; + DeploymentCreatedEvent deployment_created = 4; + DeploymentUpdatedEvent deployment_updated = 5; + IngressEvent ingress = 6; + CronScheduledEvent cron_scheduled = 7; + AsyncExecuteEvent async_execute = 8; + PubSubPublishEvent pubsub_publish = 9; + PubSubConsumeEvent pubsub_consume = 10; + } } + + repeated EventEntry entries = 1; } -message CreateEventResponse {} +message CreateEventsResponse {} message DeleteOldEventsRequest { timeline.v1.EventType event_type = 1; @@ -131,7 +139,7 @@ service TimelineService { // Stream timeline events with filters rpc StreamTimeline(StreamTimelineRequest) returns (stream StreamTimelineResponse); - rpc CreateEvent(CreateEventRequest) returns (CreateEventResponse) {} + rpc CreateEvents(CreateEventsRequest) returns (CreateEventsResponse) {} // Delete old events of a specific type rpc DeleteOldEvents(DeleteOldEventsRequest) returns (DeleteOldEventsResponse) {} diff --git a/backend/protos/xyz/block/ftl/timeline/v1/timelinev1connect/timeline.connect.go b/backend/protos/xyz/block/ftl/timeline/v1/timelinev1connect/timeline.connect.go index a09f9058fa..5596fd3ad1 100644 --- a/backend/protos/xyz/block/ftl/timeline/v1/timelinev1connect/timeline.connect.go +++ b/backend/protos/xyz/block/ftl/timeline/v1/timelinev1connect/timeline.connect.go @@ -42,9 +42,9 @@ const ( // TimelineServiceStreamTimelineProcedure is the fully-qualified name of the TimelineService's // StreamTimeline RPC. TimelineServiceStreamTimelineProcedure = "/xyz.block.ftl.timeline.v1.TimelineService/StreamTimeline" - // TimelineServiceCreateEventProcedure is the fully-qualified name of the TimelineService's - // CreateEvent RPC. - TimelineServiceCreateEventProcedure = "/xyz.block.ftl.timeline.v1.TimelineService/CreateEvent" + // TimelineServiceCreateEventsProcedure is the fully-qualified name of the TimelineService's + // CreateEvents RPC. + TimelineServiceCreateEventsProcedure = "/xyz.block.ftl.timeline.v1.TimelineService/CreateEvents" // TimelineServiceDeleteOldEventsProcedure is the fully-qualified name of the TimelineService's // DeleteOldEvents RPC. TimelineServiceDeleteOldEventsProcedure = "/xyz.block.ftl.timeline.v1.TimelineService/DeleteOldEvents" @@ -58,7 +58,7 @@ type TimelineServiceClient interface { GetTimeline(context.Context, *connect.Request[v11.GetTimelineRequest]) (*connect.Response[v11.GetTimelineResponse], error) // Stream timeline events with filters StreamTimeline(context.Context, *connect.Request[v11.StreamTimelineRequest]) (*connect.ServerStreamForClient[v11.StreamTimelineResponse], error) - CreateEvent(context.Context, *connect.Request[v11.CreateEventRequest]) (*connect.Response[v11.CreateEventResponse], error) + CreateEvents(context.Context, *connect.Request[v11.CreateEventsRequest]) (*connect.Response[v11.CreateEventsResponse], error) // Delete old events of a specific type DeleteOldEvents(context.Context, *connect.Request[v11.DeleteOldEventsRequest]) (*connect.Response[v11.DeleteOldEventsResponse], error) } @@ -90,9 +90,9 @@ func NewTimelineServiceClient(httpClient connect.HTTPClient, baseURL string, opt baseURL+TimelineServiceStreamTimelineProcedure, opts..., ), - createEvent: connect.NewClient[v11.CreateEventRequest, v11.CreateEventResponse]( + createEvents: connect.NewClient[v11.CreateEventsRequest, v11.CreateEventsResponse]( httpClient, - baseURL+TimelineServiceCreateEventProcedure, + baseURL+TimelineServiceCreateEventsProcedure, opts..., ), deleteOldEvents: connect.NewClient[v11.DeleteOldEventsRequest, v11.DeleteOldEventsResponse]( @@ -108,7 +108,7 @@ type timelineServiceClient struct { ping *connect.Client[v1.PingRequest, v1.PingResponse] getTimeline *connect.Client[v11.GetTimelineRequest, v11.GetTimelineResponse] streamTimeline *connect.Client[v11.StreamTimelineRequest, v11.StreamTimelineResponse] - createEvent *connect.Client[v11.CreateEventRequest, v11.CreateEventResponse] + createEvents *connect.Client[v11.CreateEventsRequest, v11.CreateEventsResponse] deleteOldEvents *connect.Client[v11.DeleteOldEventsRequest, v11.DeleteOldEventsResponse] } @@ -127,9 +127,9 @@ func (c *timelineServiceClient) StreamTimeline(ctx context.Context, req *connect return c.streamTimeline.CallServerStream(ctx, req) } -// CreateEvent calls xyz.block.ftl.timeline.v1.TimelineService.CreateEvent. -func (c *timelineServiceClient) CreateEvent(ctx context.Context, req *connect.Request[v11.CreateEventRequest]) (*connect.Response[v11.CreateEventResponse], error) { - return c.createEvent.CallUnary(ctx, req) +// CreateEvents calls xyz.block.ftl.timeline.v1.TimelineService.CreateEvents. +func (c *timelineServiceClient) CreateEvents(ctx context.Context, req *connect.Request[v11.CreateEventsRequest]) (*connect.Response[v11.CreateEventsResponse], error) { + return c.createEvents.CallUnary(ctx, req) } // DeleteOldEvents calls xyz.block.ftl.timeline.v1.TimelineService.DeleteOldEvents. @@ -146,7 +146,7 @@ type TimelineServiceHandler interface { GetTimeline(context.Context, *connect.Request[v11.GetTimelineRequest]) (*connect.Response[v11.GetTimelineResponse], error) // Stream timeline events with filters StreamTimeline(context.Context, *connect.Request[v11.StreamTimelineRequest], *connect.ServerStream[v11.StreamTimelineResponse]) error - CreateEvent(context.Context, *connect.Request[v11.CreateEventRequest]) (*connect.Response[v11.CreateEventResponse], error) + CreateEvents(context.Context, *connect.Request[v11.CreateEventsRequest]) (*connect.Response[v11.CreateEventsResponse], error) // Delete old events of a specific type DeleteOldEvents(context.Context, *connect.Request[v11.DeleteOldEventsRequest]) (*connect.Response[v11.DeleteOldEventsResponse], error) } @@ -174,9 +174,9 @@ func NewTimelineServiceHandler(svc TimelineServiceHandler, opts ...connect.Handl svc.StreamTimeline, opts..., ) - timelineServiceCreateEventHandler := connect.NewUnaryHandler( - TimelineServiceCreateEventProcedure, - svc.CreateEvent, + timelineServiceCreateEventsHandler := connect.NewUnaryHandler( + TimelineServiceCreateEventsProcedure, + svc.CreateEvents, opts..., ) timelineServiceDeleteOldEventsHandler := connect.NewUnaryHandler( @@ -192,8 +192,8 @@ func NewTimelineServiceHandler(svc TimelineServiceHandler, opts ...connect.Handl timelineServiceGetTimelineHandler.ServeHTTP(w, r) case TimelineServiceStreamTimelineProcedure: timelineServiceStreamTimelineHandler.ServeHTTP(w, r) - case TimelineServiceCreateEventProcedure: - timelineServiceCreateEventHandler.ServeHTTP(w, r) + case TimelineServiceCreateEventsProcedure: + timelineServiceCreateEventsHandler.ServeHTTP(w, r) case TimelineServiceDeleteOldEventsProcedure: timelineServiceDeleteOldEventsHandler.ServeHTTP(w, r) default: @@ -217,8 +217,8 @@ func (UnimplementedTimelineServiceHandler) StreamTimeline(context.Context, *conn return connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.timeline.v1.TimelineService.StreamTimeline is not implemented")) } -func (UnimplementedTimelineServiceHandler) CreateEvent(context.Context, *connect.Request[v11.CreateEventRequest]) (*connect.Response[v11.CreateEventResponse], error) { - return nil, connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.timeline.v1.TimelineService.CreateEvent is not implemented")) +func (UnimplementedTimelineServiceHandler) CreateEvents(context.Context, *connect.Request[v11.CreateEventsRequest]) (*connect.Response[v11.CreateEventsResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.timeline.v1.TimelineService.CreateEvents is not implemented")) } func (UnimplementedTimelineServiceHandler) DeleteOldEvents(context.Context, *connect.Request[v11.DeleteOldEventsRequest]) (*connect.Response[v11.DeleteOldEventsResponse], error) { diff --git a/backend/timeline/client.go b/backend/timeline/client.go new file mode 100644 index 0000000000..09d4b47d85 --- /dev/null +++ b/backend/timeline/client.go @@ -0,0 +1,122 @@ +package timeline + +import ( + "context" + "net/url" + "time" + + "connectrpc.com/connect" + "github.com/alecthomas/atomic" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/TBD54566975/ftl/backend/controller/observability" + timelinepb "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/timeline/v1" + "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/timeline/v1/timelinev1connect" + "github.com/TBD54566975/ftl/internal/log" + "github.com/TBD54566975/ftl/internal/rpc" +) + +const ( + maxBatchSize = 16 + maxBatchDelay = 100 * time.Millisecond +) + +type timelineContextKey struct{} + +type Client struct { + timelinev1connect.TimelineServiceClient + + entries chan *timelinepb.CreateEventsRequest_EventEntry + lastDroppedError atomic.Value[time.Time] + lastFailedError atomic.Value[time.Time] +} + +func NewClient(ctx context.Context, endpoint *url.URL) *Client { + c := rpc.Dial(timelinev1connect.NewTimelineServiceClient, endpoint.String(), log.Error) + client := &Client{ + TimelineServiceClient: c, + entries: make(chan *timelinepb.CreateEventsRequest_EventEntry, 1000), + } + go client.processEvents(ctx) + return client +} + +func ContextWithClient(ctx context.Context, client *Client) context.Context { + return context.WithValue(ctx, timelineContextKey{}, client) +} + +func ClientFromContext(ctx context.Context) *Client { + c, ok := ctx.Value(timelineContextKey{}).(*Client) + if !ok { + panic("Timeline client not found in context") + } + return c +} + +//go:sumtype +type Event interface { + ToEntry() (*timelinepb.CreateEventsRequest_EventEntry, error) + clientEvent() +} + +// Publish asynchronously enqueues an event for publication to the timeline. +func (c *Client) Publish(ctx context.Context, event Event) { + entry, err := event.ToEntry() + entry.Timestamp = timestamppb.New(time.Now()) + if err != nil { + log.FromContext(ctx).Warnf("failed to create request to publish %T event: %v", event, err) + return + } + select { + case c.entries <- entry: + default: + if time.Since(c.lastDroppedError.Load()) > 10*time.Second { + log.FromContext(ctx).Warnf("Dropping event %T due to full queue", event) + c.lastDroppedError.Store(time.Now()) + } + } +} + +func (c *Client) processEvents(ctx context.Context) { + lastFlush := time.Now() + buffer := make([]*timelinepb.CreateEventsRequest_EventEntry, 0, maxBatchSize) + for { + select { + case <-ctx.Done(): + return + + case entry := <-c.entries: + buffer = append(buffer, entry) + + if len(buffer) < maxBatchSize || time.Since(lastFlush) < maxBatchDelay { + continue + } + c.flushEvents(ctx, buffer) + buffer = nil + + case <-time.After(maxBatchDelay): + if len(buffer) == 0 { + continue + } + c.flushEvents(ctx, buffer) + buffer = nil + } + } +} + +// Flush all events in the buffer to the timeline service in a single call. +func (c *Client) flushEvents(ctx context.Context, entries []*timelinepb.CreateEventsRequest_EventEntry) { + logger := log.FromContext(ctx).Scope("timeline") + _, err := c.CreateEvents(ctx, connect.NewRequest(&timelinepb.CreateEventsRequest{ + Entries: entries, + })) + if err != nil { + if time.Since(c.lastFailedError.Load()) > 10*time.Second { + logger.Errorf(err, "Failed to insert %d events", len(entries)) + c.lastFailedError.Store(time.Now()) + } + observability.Timeline.Failed(ctx, len(entries)) + return + } + observability.Timeline.Inserted(ctx, len(entries)) +} diff --git a/backend/timeline/events_async.go b/backend/timeline/events_async.go index 7c139d86ee..2a4039d716 100644 --- a/backend/timeline/events_async.go +++ b/backend/timeline/events_async.go @@ -47,9 +47,9 @@ type AsyncExecute struct { var _ Event = AsyncExecute{} func (AsyncExecute) clientEvent() {} -func (a AsyncExecute) ToReq() (*timelinepb.CreateEventRequest, error) { - return &timelinepb.CreateEventRequest{ - Entry: &timelinepb.CreateEventRequest_AsyncExecute{ +func (a AsyncExecute) ToEntry() (*timelinepb.CreateEventsRequest_EventEntry, error) { + return &timelinepb.CreateEventsRequest_EventEntry{ + Entry: &timelinepb.CreateEventsRequest_EventEntry_AsyncExecute{ AsyncExecute: &timelinepb.AsyncExecuteEvent{ DeploymentKey: a.DeploymentKey.String(), RequestKey: a.RequestKey.Ptr(), diff --git a/backend/timeline/events_call.go b/backend/timeline/events_call.go index a1a0768631..89c90258df 100644 --- a/backend/timeline/events_call.go +++ b/backend/timeline/events_call.go @@ -27,7 +27,7 @@ type Call struct { } func (Call) clientEvent() {} -func (c Call) ToReq() (*timelinepb.CreateEventRequest, error) { +func (c Call) ToEntry() (*timelinepb.CreateEventsRequest_EventEntry, error) { requestKey := c.RequestKey.String() var respError *string @@ -49,8 +49,8 @@ func (c Call) ToReq() (*timelinepb.CreateEventRequest, error) { sourceVerb = c.Callers[0].ToProto().(*schemapb.Ref) //nolint:forcetypeassert } - return &timelinepb.CreateEventRequest{ - Entry: &timelinepb.CreateEventRequest_Call{ + return &timelinepb.CreateEventsRequest_EventEntry{ + Entry: &timelinepb.CreateEventsRequest_EventEntry_Call{ Call: &timelinepb.CallEvent{ RequestKey: &requestKey, DeploymentKey: c.DeploymentKey.String(), diff --git a/backend/timeline/events_cron.go b/backend/timeline/events_cron.go index 1d850e82f9..7f468ae0aa 100644 --- a/backend/timeline/events_cron.go +++ b/backend/timeline/events_cron.go @@ -25,9 +25,9 @@ type CronScheduled struct { var _ Event = CronScheduled{} func (e CronScheduled) clientEvent() {} -func (e CronScheduled) ToReq() (*timelinepb.CreateEventRequest, error) { - return &timelinepb.CreateEventRequest{ - Entry: &timelinepb.CreateEventRequest_CronScheduled{ +func (e CronScheduled) ToEntry() (*timelinepb.CreateEventsRequest_EventEntry, error) { + return &timelinepb.CreateEventsRequest_EventEntry{ + Entry: &timelinepb.CreateEventsRequest_EventEntry_CronScheduled{ CronScheduled: &timelinepb.CronScheduledEvent{ DeploymentKey: e.DeploymentKey.String(), VerbRef: (&e.Verb).ToProto().(*schemapb.Ref), //nolint:forcetypeassert diff --git a/backend/timeline/events_deployment.go b/backend/timeline/events_deployment.go index 90a7f44222..a0fb842e9d 100644 --- a/backend/timeline/events_deployment.go +++ b/backend/timeline/events_deployment.go @@ -21,14 +21,14 @@ type DeploymentCreated struct { var _ Event = DeploymentCreated{} func (DeploymentCreated) clientEvent() {} -func (d DeploymentCreated) ToReq() (*timelinepb.CreateEventRequest, error) { +func (d DeploymentCreated) ToEntry() (*timelinepb.CreateEventsRequest_EventEntry, error) { var replaced *string if r, ok := d.ReplacedDeployment.Get(); ok { repl := r.String() replaced = &repl } - return &timelinepb.CreateEventRequest{ - Entry: &timelinepb.CreateEventRequest_DeploymentCreated{ + return &timelinepb.CreateEventsRequest_EventEntry{ + Entry: &timelinepb.CreateEventsRequest_EventEntry_DeploymentCreated{ DeploymentCreated: &timelinepb.DeploymentCreatedEvent{ Key: d.DeploymentKey.String(), Language: d.Language, @@ -50,9 +50,9 @@ type DeploymentUpdated struct { var _ Event = DeploymentUpdated{} func (DeploymentUpdated) clientEvent() {} -func (d DeploymentUpdated) ToReq() (*timelinepb.CreateEventRequest, error) { - return &timelinepb.CreateEventRequest{ - Entry: &timelinepb.CreateEventRequest_DeploymentUpdated{ +func (d DeploymentUpdated) ToEntry() (*timelinepb.CreateEventsRequest_EventEntry, error) { + return &timelinepb.CreateEventsRequest_EventEntry{ + Entry: &timelinepb.CreateEventsRequest_EventEntry_DeploymentUpdated{ DeploymentUpdated: &timelinepb.DeploymentUpdatedEvent{ Key: d.DeploymentKey.String(), MinReplicas: int32(d.MinReplicas), diff --git a/backend/timeline/events_ingress.go b/backend/timeline/events_ingress.go index 98dd5863bc..b40ac667c7 100644 --- a/backend/timeline/events_ingress.go +++ b/backend/timeline/events_ingress.go @@ -34,7 +34,7 @@ type Ingress struct { var _ Event = Ingress{} func (Ingress) clientEvent() {} -func (i Ingress) ToReq() (*timelinepb.CreateEventRequest, error) { +func (i Ingress) ToEntry() (*timelinepb.CreateEventsRequest_EventEntry, error) { requestKey := i.RequestKey.String() requestBody := i.RequestBody @@ -57,8 +57,8 @@ func (i Ingress) ToReq() (*timelinepb.CreateEventRequest, error) { return nil, fmt.Errorf("failed to marshal response header: %w", err) } - return &timelinepb.CreateEventRequest{ - Entry: &timelinepb.CreateEventRequest_Ingress{ + return &timelinepb.CreateEventsRequest_EventEntry{ + Entry: &timelinepb.CreateEventsRequest_EventEntry_Ingress{ Ingress: &timelinepb.IngressEvent{ DeploymentKey: i.DeploymentKey.String(), RequestKey: &requestKey, diff --git a/backend/timeline/events_log.go b/backend/timeline/events_log.go index 6ab67af8d6..42229c974b 100644 --- a/backend/timeline/events_log.go +++ b/backend/timeline/events_log.go @@ -23,14 +23,14 @@ type Log struct { var _ Event = Log{} func (Log) clientEvent() {} -func (l Log) ToReq() (*timelinepb.CreateEventRequest, error) { +func (l Log) ToEntry() (*timelinepb.CreateEventsRequest_EventEntry, error) { var requestKey *string if r, ok := l.RequestKey.Get(); ok { key := r.String() requestKey = &key } - return &timelinepb.CreateEventRequest{ - Entry: &timelinepb.CreateEventRequest_Log{ + return &timelinepb.CreateEventsRequest_EventEntry{ + Entry: &timelinepb.CreateEventsRequest_EventEntry_Log{ Log: &timelinepb.LogEvent{ DeploymentKey: l.DeploymentKey.String(), RequestKey: requestKey, diff --git a/backend/timeline/events_pubsub_consume.go b/backend/timeline/events_pubsub_consume.go index af7a1aeecf..6eb63705cb 100644 --- a/backend/timeline/events_pubsub_consume.go +++ b/backend/timeline/events_pubsub_consume.go @@ -24,14 +24,14 @@ type PubSubConsume struct { var _ Event = PubSubConsume{} func (PubSubConsume) clientEvent() {} -func (p PubSubConsume) ToReq() (*timelinepb.CreateEventRequest, error) { +func (p PubSubConsume) ToEntry() (*timelinepb.CreateEventsRequest_EventEntry, error) { var destModule, destVerb *string if ref, ok := p.DestVerb.Get(); ok { destModule = &ref.Module destVerb = &ref.Name } - return &timelinepb.CreateEventRequest{ - Entry: &timelinepb.CreateEventRequest_PubsubConsume{ + return &timelinepb.CreateEventsRequest_EventEntry{ + Entry: &timelinepb.CreateEventsRequest_EventEntry_PubsubConsume{ PubsubConsume: &timelinepb.PubSubConsumeEvent{ DeploymentKey: p.DeploymentKey.String(), RequestKey: p.RequestKey.Ptr(), diff --git a/backend/timeline/events_pubsub_publish.go b/backend/timeline/events_pubsub_publish.go index bb8bd6c382..fc70ddf296 100644 --- a/backend/timeline/events_pubsub_publish.go +++ b/backend/timeline/events_pubsub_publish.go @@ -26,9 +26,9 @@ type PubSubPublish struct { var _ Event = PubSubPublish{} func (PubSubPublish) clientEvent() {} -func (p PubSubPublish) ToReq() (*timelinepb.CreateEventRequest, error) { - return &timelinepb.CreateEventRequest{ - Entry: &timelinepb.CreateEventRequest_PubsubPublish{ +func (p PubSubPublish) ToEntry() (*timelinepb.CreateEventsRequest_EventEntry, error) { + return &timelinepb.CreateEventsRequest_EventEntry{ + Entry: &timelinepb.CreateEventsRequest_EventEntry_PubsubPublish{ PubsubPublish: &timelinepb.PubSubPublishEvent{ DeploymentKey: p.DeploymentKey.String(), RequestKey: p.RequestKey.Ptr(), diff --git a/backend/timeline/integration_test.go b/backend/timeline/integration_test.go index 28984b8982..5bd134af2b 100644 --- a/backend/timeline/integration_test.go +++ b/backend/timeline/integration_test.go @@ -5,12 +5,19 @@ package timeline import ( "context" "net/http" + "slices" + "strconv" + "sync" "testing" + "time" + "connectrpc.com/connect" "github.com/alecthomas/assert/v2" + "google.golang.org/protobuf/types/known/timestamppb" timelinepb "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/timeline/v1" in "github.com/TBD54566975/ftl/internal/integration" + "github.com/TBD54566975/ftl/internal/log" ) func TestTimeline(t *testing.T) { @@ -140,3 +147,104 @@ func TestTimeline(t *testing.T) { ), ) } + +type streamState struct { + ascEvents []*timelinepb.Event + descEvents []*timelinepb.Event + actualEntries []*timelinepb.CreateEventsRequest_EventEntry +} + +func TestStreamTimeline(t *testing.T) { + lock := &sync.Mutex{} + state := &streamState{} + in.Run(t, + // stream events into two slices, one in ascending order and one in descending order + streamEvents(60, timelinepb.GetTimelineRequest_ORDER_ASC, state, lock), + streamEvents(60, timelinepb.GetTimelineRequest_ORDER_DESC, state, lock), + + // create events with timestamps out of order, simulating different services publishing events at different times + createOutOfOrderEvents(100, state), + in.Sleep(3*time.Second), + createOutOfOrderEvents(100, state), + in.Sleep(3*time.Second), + + // check that all events where streamed and in the correct order + checkEvents(state, true, lock), + checkEvents(state, false, lock), + ) +} + +func streamEvents(pageSize int32, order timelinepb.GetTimelineRequest_Order, state *streamState, lock *sync.Mutex) in.Action { + return func(t testing.TB, ic in.TestContext) { + in.Infof("Steaming events (order = %v)", order) + go func() { + stream, err := ic.Timeline.StreamTimeline(ic.Context, connect.NewRequest(&timelinepb.StreamTimelineRequest{ + Query: &timelinepb.GetTimelineRequest{ + Limit: pageSize, + Order: order, + }, + })) + assert.NoError(t, err) + defer stream.Close() + for stream.Receive() { + lock.Lock() + log.FromContext(ic.Context).Infof("streamed %d events", len(stream.Msg().Events)) + if order == timelinepb.GetTimelineRequest_ORDER_ASC { + state.ascEvents = append(state.ascEvents, stream.Msg().Events...) + } else { + reverseEvents := make([]*timelinepb.Event, 0, len(stream.Msg().Events)) + reverseEvents = append(reverseEvents, stream.Msg().Events...) + slices.Reverse(reverseEvents) + state.descEvents = append(state.descEvents, reverseEvents...) + } + lock.Unlock() + } + }() + } +} + +func createOutOfOrderEvents(count int, state *streamState) in.Action { + return func(t testing.TB, ic in.TestContext) { + in.Infof("Creating events") + for i := range count { + entry := &timelinepb.CreateEventsRequest_EventEntry{ + Timestamp: timestamppb.New(time.Now()), + Entry: &timelinepb.CreateEventsRequest_EventEntry_DeploymentCreated{ + DeploymentCreated: &timelinepb.DeploymentCreatedEvent{ + Key: "fake", + Language: "go", + ModuleName: "fakemodule:" + strconv.Itoa(i), + MinReplicas: 1, + }, + }, + } + _, err := ic.Timeline.CreateEvents(ic.Context, connect.NewRequest(&timelinepb.CreateEventsRequest{ + Entries: []*timelinepb.CreateEventsRequest_EventEntry{ + entry, + }, + })) + assert.NoError(t, err) + state.actualEntries = append(state.actualEntries, entry) + } + } +} + +func checkEvents(state *streamState, asc bool, lock *sync.Mutex) in.Action { + return func(t testing.TB, ic in.TestContext) { + in.Infof("Checking events (asc = %v)", asc) + lock.Lock() + defer lock.Unlock() + + var streamEvents []*timelinepb.Event + if asc { + streamEvents = state.ascEvents + } else { + streamEvents = state.descEvents + } + assert.Equal(t, len(state.actualEntries), len(streamEvents), "expected all events to have been streamed") + for i, event := range streamEvents { + expectedEntry := state.actualEntries[i] + assert.Equal(t, expectedEntry.GetDeploymentCreated().ModuleName, event.GetDeploymentCreated().ModuleName, "expected streamed event to match publication order") + } + } +} diff --git a/backend/timeline/publish.go b/backend/timeline/publish.go deleted file mode 100644 index 5c20fc7298..0000000000 --- a/backend/timeline/publish.go +++ /dev/null @@ -1,31 +0,0 @@ -package timeline - -import ( - "context" - - "connectrpc.com/connect" - - timelinepb "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/timeline/v1" - "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/timeline/v1/timelinev1connect" - "github.com/TBD54566975/ftl/internal/log" - "github.com/TBD54566975/ftl/internal/rpc" -) - -//go:sumtype -type Event interface { - ToReq() (*timelinepb.CreateEventRequest, error) - clientEvent() -} - -func Publish(ctx context.Context, event Event) { - client := rpc.ClientFromContext[timelinev1connect.TimelineServiceClient](ctx) - req, err := event.ToReq() - if err != nil { - log.FromContext(ctx).Warnf("failed to create request to publish %T event: %v", event, err) - return - } - _, err = client.CreateEvent(ctx, connect.NewRequest(req)) - if err != nil { - log.FromContext(ctx).Warnf("failed to publish %T event: %v", event, err) - } -} diff --git a/backend/timeline/service.go b/backend/timeline/service.go index e29b5f9802..30de710913 100644 --- a/backend/timeline/service.go +++ b/backend/timeline/service.go @@ -5,11 +5,13 @@ import ( "errors" "fmt" "net/url" + "sort" "sync" "time" "connectrpc.com/connect" "github.com/alecthomas/kong" + "github.com/alecthomas/types/optional" "google.golang.org/protobuf/types/known/timestamppb" timelinepb "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/timeline/v1" @@ -59,55 +61,66 @@ func (s *service) Ping(ctx context.Context, req *connect.Request[ftlv1.PingReque return connect.NewResponse(&ftlv1.PingResponse{}), nil } -func (s *service) CreateEvent(ctx context.Context, req *connect.Request[timelinepb.CreateEventRequest]) (*connect.Response[timelinepb.CreateEventResponse], error) { +func (s *service) CreateEvents(ctx context.Context, req *connect.Request[timelinepb.CreateEventsRequest]) (*connect.Response[timelinepb.CreateEventsResponse], error) { s.lock.Lock() defer s.lock.Unlock() - event := &timelinepb.Event{ - Id: int64(s.nextID), - Timestamp: timestamppb.Now(), - } - switch entry := req.Msg.Entry.(type) { - case *timelinepb.CreateEventRequest_Log: - event.Entry = &timelinepb.Event_Log{ - Log: entry.Log, - } - case *timelinepb.CreateEventRequest_Call: - event.Entry = &timelinepb.Event_Call{ - Call: entry.Call, - } - case *timelinepb.CreateEventRequest_DeploymentCreated: - event.Entry = &timelinepb.Event_DeploymentCreated{ - DeploymentCreated: entry.DeploymentCreated, - } - case *timelinepb.CreateEventRequest_DeploymentUpdated: - event.Entry = &timelinepb.Event_DeploymentUpdated{ - DeploymentUpdated: entry.DeploymentUpdated, - } - case *timelinepb.CreateEventRequest_Ingress: - event.Entry = &timelinepb.Event_Ingress{ - Ingress: entry.Ingress, - } - case *timelinepb.CreateEventRequest_CronScheduled: - event.Entry = &timelinepb.Event_CronScheduled{ - CronScheduled: entry.CronScheduled, - } - case *timelinepb.CreateEventRequest_AsyncExecute: - event.Entry = &timelinepb.Event_AsyncExecute{ - AsyncExecute: entry.AsyncExecute, + entries := make([]*timelinepb.CreateEventsRequest_EventEntry, 0, len(req.Msg.Entries)) + entries = append(entries, req.Msg.Entries...) + sort.Slice(entries, func(i, j int) bool { + return entries[i].Timestamp.AsTime().Before(entries[j].Timestamp.AsTime()) + }) + + for _, entry := range entries { + if entry.Timestamp == nil { + return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("timestamp is required")) } - case *timelinepb.CreateEventRequest_PubsubPublish: - event.Entry = &timelinepb.Event_PubsubPublish{ - PubsubPublish: entry.PubsubPublish, + event := &timelinepb.Event{ + Id: int64(s.nextID), + Timestamp: entry.Timestamp, } - case *timelinepb.CreateEventRequest_PubsubConsume: - event.Entry = &timelinepb.Event_PubsubConsume{ - PubsubConsume: entry.PubsubConsume, + switch entry := entry.Entry.(type) { + case *timelinepb.CreateEventsRequest_EventEntry_Log: + event.Entry = &timelinepb.Event_Log{ + Log: entry.Log, + } + case *timelinepb.CreateEventsRequest_EventEntry_Call: + event.Entry = &timelinepb.Event_Call{ + Call: entry.Call, + } + case *timelinepb.CreateEventsRequest_EventEntry_DeploymentCreated: + event.Entry = &timelinepb.Event_DeploymentCreated{ + DeploymentCreated: entry.DeploymentCreated, + } + case *timelinepb.CreateEventsRequest_EventEntry_DeploymentUpdated: + event.Entry = &timelinepb.Event_DeploymentUpdated{ + DeploymentUpdated: entry.DeploymentUpdated, + } + case *timelinepb.CreateEventsRequest_EventEntry_Ingress: + event.Entry = &timelinepb.Event_Ingress{ + Ingress: entry.Ingress, + } + case *timelinepb.CreateEventsRequest_EventEntry_CronScheduled: + event.Entry = &timelinepb.Event_CronScheduled{ + CronScheduled: entry.CronScheduled, + } + case *timelinepb.CreateEventsRequest_EventEntry_AsyncExecute: + event.Entry = &timelinepb.Event_AsyncExecute{ + AsyncExecute: entry.AsyncExecute, + } + case *timelinepb.CreateEventsRequest_EventEntry_PubsubPublish: + event.Entry = &timelinepb.Event_PubsubPublish{ + PubsubPublish: entry.PubsubPublish, + } + case *timelinepb.CreateEventsRequest_EventEntry_PubsubConsume: + event.Entry = &timelinepb.Event_PubsubConsume{ + PubsubConsume: entry.PubsubConsume, + } } + s.events = append(s.events, event) + s.nextID++ } - s.events = append(s.events, event) - s.nextID++ - return connect.NewResponse(&timelinepb.CreateEventResponse{}), nil + return connect.NewResponse(&timelinepb.CreateEventsResponse{}), nil } func (s *service) GetTimeline(ctx context.Context, req *connect.Request[timelinepb.GetTimelineRequest]) (*connect.Response[timelinepb.GetTimelineResponse], error) { @@ -173,19 +186,20 @@ func (s *service) StreamTimeline(ctx context.Context, req *connect.Request[timel return connect.NewError(connect.CodeInvalidArgument, errors.New("limit must be > 0")) } + _, ascending := filtersFromRequest(req.Msg.Query) + timelineReq := req.Msg.Query // Default to last 1 day of events - var lastEventTime time.Time + var lastEventID optional.Option[int64] for { - thisRequestTime := time.Now() newQuery := timelineReq - - if !lastEventTime.IsZero() { + // We always want ascending order for the underlying query. + newQuery.Order = timelinepb.GetTimelineRequest_ORDER_ASC + if _, ok := lastEventID.Get(); ok { newQuery.Filters = append(newQuery.Filters, &timelinepb.GetTimelineRequest_Filter{ - Filter: &timelinepb.GetTimelineRequest_Filter_Time{ - Time: &timelinepb.GetTimelineRequest_TimeFilter{ - NewerThan: timestamppb.New(lastEventTime), - OlderThan: timestamppb.New(thisRequestTime), + Filter: &timelinepb.GetTimelineRequest_Filter_Id{ + Id: &timelinepb.GetTimelineRequest_IDFilter{ + HigherThan: lastEventID.Ptr(), }, }, }) @@ -196,16 +210,28 @@ func (s *service) StreamTimeline(ctx context.Context, req *connect.Request[timel return fmt.Errorf("failed to get timeline: %w", err) } - if len(resp.Msg.Events) > 0 { + newEvents := make([]*timelinepb.Event, 0, len(resp.Msg.Events)) + for _, event := range resp.Msg.Events { + if lastEventID, ok := lastEventID.Get(); !ok || event.Id != lastEventID { + // This is not a duplicate event. + newEvents = append(newEvents, event) + } + } + if len(newEvents) > 0 { + lastEventID = optional.Some(newEvents[len(newEvents)-1].Id) + + if !ascending { + // Original query was for descending order, so reverse the events. + slices.Reverse(newEvents) + } err = stream.Send(&timelinepb.StreamTimelineResponse{ - Events: resp.Msg.Events, + Events: newEvents, }) if err != nil { return fmt.Errorf("failed to get timeline events: %w", err) } - } - lastEventTime = thisRequestTime + } select { case <-time.After(updateInterval): case <-ctx.Done(): diff --git a/backend/timeline/service_test.go b/backend/timeline/service_test.go index b598c66fc2..7b2ab9aeb9 100644 --- a/backend/timeline/service_test.go +++ b/backend/timeline/service_test.go @@ -9,6 +9,7 @@ import ( "connectrpc.com/connect" timelinepb "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/timeline/v1" "github.com/alecthomas/assert/v2" + "google.golang.org/protobuf/types/known/timestamppb" ) func TestGetTimelineWithLimit(t *testing.T) { @@ -18,10 +19,11 @@ func TestGetTimelineWithLimit(t *testing.T) { // Create a bunch of entries entryCount := 100 - requests := []*timelinepb.CreateEventRequest{} + entries := []*timelinepb.CreateEventsRequest_EventEntry{} for i := range entryCount { - requests = append(requests, &timelinepb.CreateEventRequest{ - Entry: &timelinepb.CreateEventRequest_Call{ + entries = append(entries, &timelinepb.CreateEventsRequest_EventEntry{ + Timestamp: timestamppb.New(time.Now()), + Entry: &timelinepb.CreateEventsRequest_EventEntry_Call{ Call: &timelinepb.CallEvent{ Request: strconv.Itoa(i), Response: strconv.Itoa(i), @@ -29,10 +31,11 @@ func TestGetTimelineWithLimit(t *testing.T) { }, }) } - for _, request := range requests { - _, err := service.CreateEvent(ctx, connect.NewRequest(request)) - assert.NoError(t, err) - } + + _, err := service.CreateEvents(ctx, connect.NewRequest(&timelinepb.CreateEventsRequest{ + Entries: entries, + })) + assert.NoError(t, err) // Test with different limits for _, limit := range []int32{ @@ -75,40 +78,46 @@ func TestDeleteOldEvents(t *testing.T) { service := &service{} // Create a bunch of entries of different types - requests := []*timelinepb.CreateEventRequest{} + entries := []*timelinepb.CreateEventsRequest_EventEntry{} for i := range 100 { - requests = append(requests, &timelinepb.CreateEventRequest{ - Entry: &timelinepb.CreateEventRequest_Call{ + var timestamp *timestamppb.Timestamp + if i < 50 { + timestamp = timestamppb.New(time.Now().Add(-3 * time.Second)) + } else { + timestamp = timestamppb.New(time.Now()) + } + entries = append(entries, &timelinepb.CreateEventsRequest_EventEntry{ + Timestamp: timestamp, + Entry: &timelinepb.CreateEventsRequest_EventEntry_Call{ Call: &timelinepb.CallEvent{ Request: strconv.Itoa(i), Response: strconv.Itoa(i), }, }, - }, &timelinepb.CreateEventRequest{ - Entry: &timelinepb.CreateEventRequest_Log{ + }, &timelinepb.CreateEventsRequest_EventEntry{ + Timestamp: timestamp, + Entry: &timelinepb.CreateEventsRequest_EventEntry_Log{ Log: &timelinepb.LogEvent{ Message: strconv.Itoa(i), }, }, - }, &timelinepb.CreateEventRequest{ - Entry: &timelinepb.CreateEventRequest_DeploymentCreated{ + }, &timelinepb.CreateEventsRequest_EventEntry{ + Timestamp: timestamp, + Entry: &timelinepb.CreateEventsRequest_EventEntry_DeploymentCreated{ DeploymentCreated: &timelinepb.DeploymentCreatedEvent{ Key: strconv.Itoa(i), }, }, }) } - for i, request := range requests { - if i == 150 { - // Add a delay half way through - time.Sleep(3 * time.Second) - } - _, err := service.CreateEvent(ctx, connect.NewRequest(request)) - assert.NoError(t, err) - } + + _, err := service.CreateEvents(ctx, connect.NewRequest(&timelinepb.CreateEventsRequest{ + Entries: entries, + })) + assert.NoError(t, err) // Delete half the events (everything older than 3 seconds) - _, err := service.DeleteOldEvents(ctx, connect.NewRequest(&timelinepb.DeleteOldEventsRequest{ + _, err = service.DeleteOldEvents(ctx, connect.NewRequest(&timelinepb.DeleteOldEventsRequest{ AgeSeconds: 3, EventType: timelinepb.EventType_EVENT_TYPE_UNSPECIFIED, })) diff --git a/cmd/ftl-controller/main.go b/cmd/ftl-controller/main.go index 19565ea951..76ab263dea 100644 --- a/cmd/ftl-controller/main.go +++ b/cmd/ftl-controller/main.go @@ -13,7 +13,7 @@ import ( "github.com/TBD54566975/ftl/backend/controller" "github.com/TBD54566975/ftl/backend/controller/artefacts" "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/lease/v1/ftlv1connect" - "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/timeline/v1/timelinev1connect" + "github.com/TBD54566975/ftl/backend/timeline" _ "github.com/TBD54566975/ftl/internal/automaxprocs" // Set GOMAXPROCS to match Linux container CPU quota. cf "github.com/TBD54566975/ftl/internal/configuration" cfdal "github.com/TBD54566975/ftl/internal/configuration/dal" @@ -68,8 +68,7 @@ func main() { cm, err := manager.New(ctx, configResolver, providers.NewDatabaseConfig(configDal)) kctx.FatalIfErrorf(err) - timelineServiceClient := rpc.Dial(timelinev1connect.NewTimelineServiceClient, cli.TimelineEndpoint.String(), log.Error) - ctx = rpc.ContextWithClient(ctx, timelineServiceClient) + ctx = timeline.ContextWithClient(ctx, timeline.NewClient(ctx, cli.TimelineEndpoint)) leaseClient := rpc.Dial(ftlv1connect.NewLeaseServiceClient, cli.LeaseEndpoint.String(), log.Error) ctx = rpc.ContextWithClient(ctx, leaseClient) diff --git a/cmd/ftl-cron/main.go b/cmd/ftl-cron/main.go index e29c7b9f90..dc8954717a 100644 --- a/cmd/ftl-cron/main.go +++ b/cmd/ftl-cron/main.go @@ -35,8 +35,7 @@ func main() { err := observability.Init(ctx, false, "", "ftl-cron", ftl.Version, cli.ObservabilityConfig) kctx.FatalIfErrorf(err, "failed to initialize observability") - // timelineServiceClient := rpc.Dial(timelinev1connect.NewTimelineServiceClient, cli.CronConfig.TimelineEndpoint.String(), log.Error) - // ctx = rpc.ContextWithClient(ctx, timelineServiceClient) + // ctx = timeline.ContextWithClient(ctx, timeline.NewClient(ctx, cli.TimelineEndpoint)) schemaClient := rpc.Dial(ftlv1connect.NewSchemaServiceClient, cli.CronConfig.SchemaServiceEndpoint.String(), log.Error) eventSource := schemaeventsource.New(ctx, schemaClient) diff --git a/cmd/ftl-http-ingress/main.go b/cmd/ftl-http-ingress/main.go index fa1ad4474e..4db1a5cd7d 100644 --- a/cmd/ftl-http-ingress/main.go +++ b/cmd/ftl-http-ingress/main.go @@ -9,8 +9,8 @@ import ( "github.com/TBD54566975/ftl" "github.com/TBD54566975/ftl/backend/ingress" - "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/timeline/v1/timelinev1connect" "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/v1/ftlv1connect" + "github.com/TBD54566975/ftl/backend/timeline" _ "github.com/TBD54566975/ftl/internal/automaxprocs" // Set GOMAXPROCS to match Linux container CPU quota. "github.com/TBD54566975/ftl/internal/log" "github.com/TBD54566975/ftl/internal/observability" @@ -39,8 +39,7 @@ func main() { err := observability.Init(ctx, false, "", "ftl-http-ingress", ftl.Version, cli.ObservabilityConfig) kctx.FatalIfErrorf(err, "failed to initialize observability") - timelineClient := rpc.Dial(timelinev1connect.NewTimelineServiceClient, cli.TimelineEndpoint.String(), log.Error) - ctx = rpc.ContextWithClient(ctx, timelineClient) + ctx = timeline.ContextWithClient(ctx, timeline.NewClient(ctx, cli.TimelineEndpoint)) schemaClient := rpc.Dial(ftlv1connect.NewSchemaServiceClient, cli.SchemaServerEndpoint.String(), log.Error) eventSource := schemaeventsource.New(ctx, schemaClient) diff --git a/frontend/cli/cmd_replay.go b/frontend/cli/cmd_replay.go index 59827ad279..9188fa5794 100644 --- a/frontend/cli/cmd_replay.go +++ b/frontend/cli/cmd_replay.go @@ -10,9 +10,9 @@ import ( "github.com/jpillora/backoff" timelinepb "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/timeline/v1" - "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/timeline/v1/timelinev1connect" ftlv1 "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/v1" "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/v1/ftlv1connect" + "github.com/TBD54566975/ftl/backend/timeline" "github.com/TBD54566975/ftl/go-runtime/ftl/reflection" "github.com/TBD54566975/ftl/internal/log" "github.com/TBD54566975/ftl/internal/rpc" @@ -37,7 +37,7 @@ func (c *replayCmd) Run( return fmt.Errorf("failed to wait for client: %w", err) } - timelineClient := rpc.Dial(timelinev1connect.NewTimelineServiceClient, cli.TimelineEndpoint.String(), log.Error) + timelineClient := timeline.ClientFromContext(ctx) if err := rpc.Wait(ctx, backoff.Backoff{Max: time.Second * 2}, c.Wait-time.Since(startTime), timelineClient); err != nil { return fmt.Errorf("failed to wait for console service client: %w", err) } diff --git a/frontend/cli/main.go b/frontend/cli/main.go index 24115b4dfa..43b4457091 100644 --- a/frontend/cli/main.go +++ b/frontend/cli/main.go @@ -21,8 +21,8 @@ import ( "github.com/TBD54566975/ftl/backend/controller/admin" leasev1connext "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/lease/v1/ftlv1connect" provisionerconnect "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/provisioner/v1beta1/provisionerpbconnect" - "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/timeline/v1/timelinev1connect" "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/v1/ftlv1connect" + "github.com/TBD54566975/ftl/backend/timeline" "github.com/TBD54566975/ftl/internal" _ "github.com/TBD54566975/ftl/internal/automaxprocs" // Set GOMAXPROCS to match Linux container CPU quota. "github.com/TBD54566975/ftl/internal/configuration" @@ -235,9 +235,8 @@ func makeBindContext(logger *log.Logger, cancel context.CancelFunc) terminal.Kon ctx = rpc.ContextWithClient(ctx, provisionerServiceClient) kctx.BindTo(provisionerServiceClient, (*provisionerconnect.ProvisionerServiceClient)(nil)) - timelineServiceClient := rpc.Dial(timelinev1connect.NewTimelineServiceClient, cli.TimelineEndpoint.String(), log.Error) - ctx = rpc.ContextWithClient(ctx, timelineServiceClient) - kctx.BindTo(timelineServiceClient, (*timelinev1connect.TimelineServiceClient)(nil)) + ctx = timeline.ContextWithClient(ctx, timeline.NewClient(ctx, cli.TimelineEndpoint)) + leaseClient := rpc.Dial(leasev1connext.NewLeaseServiceClient, cli.LeaseEndpoint.String(), log.Error) ctx = rpc.ContextWithClient(ctx, leaseClient) kctx.BindTo(leaseClient, (*leasev1connext.LeaseServiceClient)(nil)) diff --git a/frontend/console/src/protos/xyz/block/ftl/timeline/v1/timeline_connect.ts b/frontend/console/src/protos/xyz/block/ftl/timeline/v1/timeline_connect.ts index 69443d210d..fb526180f6 100644 --- a/frontend/console/src/protos/xyz/block/ftl/timeline/v1/timeline_connect.ts +++ b/frontend/console/src/protos/xyz/block/ftl/timeline/v1/timeline_connect.ts @@ -5,7 +5,7 @@ import { PingRequest, PingResponse } from "../../v1/ftl_pb.js"; import { MethodIdempotency, MethodKind } from "@bufbuild/protobuf"; -import { CreateEventRequest, CreateEventResponse, DeleteOldEventsRequest, DeleteOldEventsResponse, GetTimelineRequest, GetTimelineResponse, StreamTimelineRequest, StreamTimelineResponse } from "./timeline_pb.js"; +import { CreateEventsRequest, CreateEventsResponse, DeleteOldEventsRequest, DeleteOldEventsResponse, GetTimelineRequest, GetTimelineResponse, StreamTimelineRequest, StreamTimelineResponse } from "./timeline_pb.js"; /** * @generated from service xyz.block.ftl.timeline.v1.TimelineService @@ -49,12 +49,12 @@ export const TimelineService = { kind: MethodKind.ServerStreaming, }, /** - * @generated from rpc xyz.block.ftl.timeline.v1.TimelineService.CreateEvent + * @generated from rpc xyz.block.ftl.timeline.v1.TimelineService.CreateEvents */ - createEvent: { - name: "CreateEvent", - I: CreateEventRequest, - O: CreateEventResponse, + createEvents: { + name: "CreateEvents", + I: CreateEventsRequest, + O: CreateEventsResponse, kind: MethodKind.Unary, }, /** diff --git a/frontend/console/src/protos/xyz/block/ftl/timeline/v1/timeline_pb.ts b/frontend/console/src/protos/xyz/block/ftl/timeline/v1/timeline_pb.ts index b318d5f452..c556335785 100644 --- a/frontend/console/src/protos/xyz/block/ftl/timeline/v1/timeline_pb.ts +++ b/frontend/console/src/protos/xyz/block/ftl/timeline/v1/timeline_pb.ts @@ -22,6 +22,9 @@ export class GetTimelineRequest extends Message { limit = 0; /** + * Ordering is done by id which matches publication order. + * This roughly corresponds to the time of the event, but not strictly. + * * @generated from field: xyz.block.ftl.timeline.v1.GetTimelineRequest.Order order = 3; */ order = GetTimelineRequest_Order.UNSPECIFIED; @@ -646,132 +649,175 @@ export class StreamTimelineResponse extends Message { } /** - * @generated from message xyz.block.ftl.timeline.v1.CreateEventRequest + * @generated from message xyz.block.ftl.timeline.v1.CreateEventsRequest + */ +export class CreateEventsRequest extends Message { + /** + * @generated from field: repeated xyz.block.ftl.timeline.v1.CreateEventsRequest.EventEntry entries = 1; + */ + entries: CreateEventsRequest_EventEntry[] = []; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.timeline.v1.CreateEventsRequest"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "entries", kind: "message", T: CreateEventsRequest_EventEntry, repeated: true }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): CreateEventsRequest { + return new CreateEventsRequest().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): CreateEventsRequest { + return new CreateEventsRequest().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): CreateEventsRequest { + return new CreateEventsRequest().fromJsonString(jsonString, options); + } + + static equals(a: CreateEventsRequest | PlainMessage | undefined, b: CreateEventsRequest | PlainMessage | undefined): boolean { + return proto3.util.equals(CreateEventsRequest, a, b); + } +} + +/** + * @generated from message xyz.block.ftl.timeline.v1.CreateEventsRequest.EventEntry */ -export class CreateEventRequest extends Message { +export class CreateEventsRequest_EventEntry extends Message { + /** + * @generated from field: google.protobuf.Timestamp timestamp = 1; + */ + timestamp?: Timestamp; + /** - * @generated from oneof xyz.block.ftl.timeline.v1.CreateEventRequest.entry + * @generated from oneof xyz.block.ftl.timeline.v1.CreateEventsRequest.EventEntry.entry */ entry: { /** - * @generated from field: xyz.block.ftl.timeline.v1.LogEvent log = 1; + * @generated from field: xyz.block.ftl.timeline.v1.LogEvent log = 2; */ value: LogEvent; case: "log"; } | { /** - * @generated from field: xyz.block.ftl.timeline.v1.CallEvent call = 2; + * @generated from field: xyz.block.ftl.timeline.v1.CallEvent call = 3; */ value: CallEvent; case: "call"; } | { /** - * @generated from field: xyz.block.ftl.timeline.v1.DeploymentCreatedEvent deployment_created = 3; + * @generated from field: xyz.block.ftl.timeline.v1.DeploymentCreatedEvent deployment_created = 4; */ value: DeploymentCreatedEvent; case: "deploymentCreated"; } | { /** - * @generated from field: xyz.block.ftl.timeline.v1.DeploymentUpdatedEvent deployment_updated = 4; + * @generated from field: xyz.block.ftl.timeline.v1.DeploymentUpdatedEvent deployment_updated = 5; */ value: DeploymentUpdatedEvent; case: "deploymentUpdated"; } | { /** - * @generated from field: xyz.block.ftl.timeline.v1.IngressEvent ingress = 5; + * @generated from field: xyz.block.ftl.timeline.v1.IngressEvent ingress = 6; */ value: IngressEvent; case: "ingress"; } | { /** - * @generated from field: xyz.block.ftl.timeline.v1.CronScheduledEvent cron_scheduled = 6; + * @generated from field: xyz.block.ftl.timeline.v1.CronScheduledEvent cron_scheduled = 7; */ value: CronScheduledEvent; case: "cronScheduled"; } | { /** - * @generated from field: xyz.block.ftl.timeline.v1.AsyncExecuteEvent async_execute = 7; + * @generated from field: xyz.block.ftl.timeline.v1.AsyncExecuteEvent async_execute = 8; */ value: AsyncExecuteEvent; case: "asyncExecute"; } | { /** - * @generated from field: xyz.block.ftl.timeline.v1.PubSubPublishEvent pubsub_publish = 8; + * @generated from field: xyz.block.ftl.timeline.v1.PubSubPublishEvent pubsub_publish = 9; */ value: PubSubPublishEvent; case: "pubsubPublish"; } | { /** - * @generated from field: xyz.block.ftl.timeline.v1.PubSubConsumeEvent pubsub_consume = 9; + * @generated from field: xyz.block.ftl.timeline.v1.PubSubConsumeEvent pubsub_consume = 10; */ value: PubSubConsumeEvent; case: "pubsubConsume"; } | { case: undefined; value?: undefined } = { case: undefined }; - constructor(data?: PartialMessage) { + constructor(data?: PartialMessage) { super(); proto3.util.initPartial(data, this); } static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "xyz.block.ftl.timeline.v1.CreateEventRequest"; + static readonly typeName = "xyz.block.ftl.timeline.v1.CreateEventsRequest.EventEntry"; static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "log", kind: "message", T: LogEvent, oneof: "entry" }, - { no: 2, name: "call", kind: "message", T: CallEvent, oneof: "entry" }, - { no: 3, name: "deployment_created", kind: "message", T: DeploymentCreatedEvent, oneof: "entry" }, - { no: 4, name: "deployment_updated", kind: "message", T: DeploymentUpdatedEvent, oneof: "entry" }, - { no: 5, name: "ingress", kind: "message", T: IngressEvent, oneof: "entry" }, - { no: 6, name: "cron_scheduled", kind: "message", T: CronScheduledEvent, oneof: "entry" }, - { no: 7, name: "async_execute", kind: "message", T: AsyncExecuteEvent, oneof: "entry" }, - { no: 8, name: "pubsub_publish", kind: "message", T: PubSubPublishEvent, oneof: "entry" }, - { no: 9, name: "pubsub_consume", kind: "message", T: PubSubConsumeEvent, oneof: "entry" }, + { no: 1, name: "timestamp", kind: "message", T: Timestamp }, + { no: 2, name: "log", kind: "message", T: LogEvent, oneof: "entry" }, + { no: 3, name: "call", kind: "message", T: CallEvent, oneof: "entry" }, + { no: 4, name: "deployment_created", kind: "message", T: DeploymentCreatedEvent, oneof: "entry" }, + { no: 5, name: "deployment_updated", kind: "message", T: DeploymentUpdatedEvent, oneof: "entry" }, + { no: 6, name: "ingress", kind: "message", T: IngressEvent, oneof: "entry" }, + { no: 7, name: "cron_scheduled", kind: "message", T: CronScheduledEvent, oneof: "entry" }, + { no: 8, name: "async_execute", kind: "message", T: AsyncExecuteEvent, oneof: "entry" }, + { no: 9, name: "pubsub_publish", kind: "message", T: PubSubPublishEvent, oneof: "entry" }, + { no: 10, name: "pubsub_consume", kind: "message", T: PubSubConsumeEvent, oneof: "entry" }, ]); - static fromBinary(bytes: Uint8Array, options?: Partial): CreateEventRequest { - return new CreateEventRequest().fromBinary(bytes, options); + static fromBinary(bytes: Uint8Array, options?: Partial): CreateEventsRequest_EventEntry { + return new CreateEventsRequest_EventEntry().fromBinary(bytes, options); } - static fromJson(jsonValue: JsonValue, options?: Partial): CreateEventRequest { - return new CreateEventRequest().fromJson(jsonValue, options); + static fromJson(jsonValue: JsonValue, options?: Partial): CreateEventsRequest_EventEntry { + return new CreateEventsRequest_EventEntry().fromJson(jsonValue, options); } - static fromJsonString(jsonString: string, options?: Partial): CreateEventRequest { - return new CreateEventRequest().fromJsonString(jsonString, options); + static fromJsonString(jsonString: string, options?: Partial): CreateEventsRequest_EventEntry { + return new CreateEventsRequest_EventEntry().fromJsonString(jsonString, options); } - static equals(a: CreateEventRequest | PlainMessage | undefined, b: CreateEventRequest | PlainMessage | undefined): boolean { - return proto3.util.equals(CreateEventRequest, a, b); + static equals(a: CreateEventsRequest_EventEntry | PlainMessage | undefined, b: CreateEventsRequest_EventEntry | PlainMessage | undefined): boolean { + return proto3.util.equals(CreateEventsRequest_EventEntry, a, b); } } /** - * @generated from message xyz.block.ftl.timeline.v1.CreateEventResponse + * @generated from message xyz.block.ftl.timeline.v1.CreateEventsResponse */ -export class CreateEventResponse extends Message { - constructor(data?: PartialMessage) { +export class CreateEventsResponse extends Message { + constructor(data?: PartialMessage) { super(); proto3.util.initPartial(data, this); } static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "xyz.block.ftl.timeline.v1.CreateEventResponse"; + static readonly typeName = "xyz.block.ftl.timeline.v1.CreateEventsResponse"; static readonly fields: FieldList = proto3.util.newFieldList(() => [ ]); - static fromBinary(bytes: Uint8Array, options?: Partial): CreateEventResponse { - return new CreateEventResponse().fromBinary(bytes, options); + static fromBinary(bytes: Uint8Array, options?: Partial): CreateEventsResponse { + return new CreateEventsResponse().fromBinary(bytes, options); } - static fromJson(jsonValue: JsonValue, options?: Partial): CreateEventResponse { - return new CreateEventResponse().fromJson(jsonValue, options); + static fromJson(jsonValue: JsonValue, options?: Partial): CreateEventsResponse { + return new CreateEventsResponse().fromJson(jsonValue, options); } - static fromJsonString(jsonString: string, options?: Partial): CreateEventResponse { - return new CreateEventResponse().fromJsonString(jsonString, options); + static fromJsonString(jsonString: string, options?: Partial): CreateEventsResponse { + return new CreateEventsResponse().fromJsonString(jsonString, options); } - static equals(a: CreateEventResponse | PlainMessage | undefined, b: CreateEventResponse | PlainMessage | undefined): boolean { - return proto3.util.equals(CreateEventResponse, a, b); + static equals(a: CreateEventsResponse | PlainMessage | undefined, b: CreateEventsResponse | PlainMessage | undefined): boolean { + return proto3.util.equals(CreateEventsResponse, a, b); } } diff --git a/internal/slices/slices.go b/internal/slices/slices.go index 3dcb017128..6d2343e12f 100644 --- a/internal/slices/slices.go +++ b/internal/slices/slices.go @@ -77,6 +77,13 @@ func Sort[T cmp.Ordered](slice []T) []T { return out } +// Reverse reverses a slice of any type in place. +func Reverse[T any](slice []T) { + for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 { + slice[i], slice[j] = slice[j], slice[i] + } +} + func FlatMap[T, U any](slice []T, fn func(T) []U) []U { result := make([]U, 0, len(slice)) for _, v := range slice { diff --git a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/timeline/v1/timeline_pb2.py b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/timeline/v1/timeline_pb2.py index e11ff02f49..eb8eb72abc 100644 --- a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/timeline/v1/timeline_pb2.py +++ b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/timeline/v1/timeline_pb2.py @@ -28,7 +28,7 @@ from xyz.block.ftl.v1 import ftl_pb2 as xyz_dot_block_dot_ftl_dot_v1_dot_ftl__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(xyz/block/ftl/timeline/v1/timeline.proto\x12\x19xyz.block.ftl.timeline.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a%xyz/block/ftl/timeline/v1/event.proto\x1a\x1axyz/block/ftl/v1/ftl.proto\"\xf0\r\n\x12GetTimelineRequest\x12N\n\x07\x66ilters\x18\x01 \x03(\x0b\x32\x34.xyz.block.ftl.timeline.v1.GetTimelineRequest.FilterR\x07\x66ilters\x12\x14\n\x05limit\x18\x02 \x01(\x05R\x05limit\x12I\n\x05order\x18\x03 \x01(\x0e\x32\x33.xyz.block.ftl.timeline.v1.GetTimelineRequest.OrderR\x05order\x1aR\n\x0eLogLevelFilter\x12@\n\tlog_level\x18\x01 \x01(\x0e\x32#.xyz.block.ftl.timeline.v1.LogLevelR\x08logLevel\x1a\x34\n\x10\x44\x65ploymentFilter\x12 \n\x0b\x64\x65ployments\x18\x01 \x03(\tR\x0b\x64\x65ployments\x1a+\n\rRequestFilter\x12\x1a\n\x08requests\x18\x01 \x03(\tR\x08requests\x1aX\n\x0f\x45ventTypeFilter\x12\x45\n\x0b\x65vent_types\x18\x01 \x03(\x0e\x32$.xyz.block.ftl.timeline.v1.EventTypeR\neventTypes\x1a\xaa\x01\n\nTimeFilter\x12>\n\nolder_than\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x00R\tolderThan\x88\x01\x01\x12>\n\nnewer_than\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x01R\tnewerThan\x88\x01\x01\x42\r\n\x0b_older_thanB\r\n\x0b_newer_than\x1as\n\x08IDFilter\x12\"\n\nlower_than\x18\x01 \x01(\x03H\x00R\tlowerThan\x88\x01\x01\x12$\n\x0bhigher_than\x18\x02 \x01(\x03H\x01R\nhigherThan\x88\x01\x01\x42\r\n\x0b_lower_thanB\x0e\n\x0c_higher_than\x1a\x99\x01\n\nCallFilter\x12\x1f\n\x0b\x64\x65st_module\x18\x01 \x01(\tR\ndestModule\x12 \n\tdest_verb\x18\x02 \x01(\tH\x00R\x08\x64\x65stVerb\x88\x01\x01\x12(\n\rsource_module\x18\x03 \x01(\tH\x01R\x0csourceModule\x88\x01\x01\x42\x0c\n\n_dest_verbB\x10\n\x0e_source_module\x1aH\n\x0cModuleFilter\x12\x16\n\x06module\x18\x01 \x01(\tR\x06module\x12\x17\n\x04verb\x18\x02 \x01(\tH\x00R\x04verb\x88\x01\x01\x42\x07\n\x05_verb\x1a\xd0\x05\n\x06\x46ilter\x12[\n\tlog_level\x18\x01 \x01(\x0b\x32<.xyz.block.ftl.timeline.v1.GetTimelineRequest.LogLevelFilterH\x00R\x08logLevel\x12\x62\n\x0b\x64\x65ployments\x18\x02 \x01(\x0b\x32>.xyz.block.ftl.timeline.v1.GetTimelineRequest.DeploymentFilterH\x00R\x0b\x64\x65ployments\x12Y\n\x08requests\x18\x03 \x01(\x0b\x32;.xyz.block.ftl.timeline.v1.GetTimelineRequest.RequestFilterH\x00R\x08requests\x12`\n\x0b\x65vent_types\x18\x04 \x01(\x0b\x32=.xyz.block.ftl.timeline.v1.GetTimelineRequest.EventTypeFilterH\x00R\neventTypes\x12N\n\x04time\x18\x05 \x01(\x0b\x32\x38.xyz.block.ftl.timeline.v1.GetTimelineRequest.TimeFilterH\x00R\x04time\x12H\n\x02id\x18\x06 \x01(\x0b\x32\x36.xyz.block.ftl.timeline.v1.GetTimelineRequest.IDFilterH\x00R\x02id\x12N\n\x04\x63\x61ll\x18\x07 \x01(\x0b\x32\x38.xyz.block.ftl.timeline.v1.GetTimelineRequest.CallFilterH\x00R\x04\x63\x61ll\x12T\n\x06module\x18\x08 \x01(\x0b\x32:.xyz.block.ftl.timeline.v1.GetTimelineRequest.ModuleFilterH\x00R\x06moduleB\x08\n\x06\x66ilter\"=\n\x05Order\x12\x15\n\x11ORDER_UNSPECIFIED\x10\x00\x12\r\n\tORDER_ASC\x10\x01\x12\x0e\n\nORDER_DESC\x10\x02\"w\n\x13GetTimelineResponse\x12\x38\n\x06\x65vents\x18\x01 \x03(\x0b\x32 .xyz.block.ftl.timeline.v1.EventR\x06\x65vents\x12\x1b\n\x06\x63ursor\x18\x02 \x01(\x03H\x00R\x06\x63ursor\x88\x01\x01\x42\t\n\x07_cursor\"\xb9\x01\n\x15StreamTimelineRequest\x12G\n\x0fupdate_interval\x18\x01 \x01(\x0b\x32\x19.google.protobuf.DurationH\x00R\x0eupdateInterval\x88\x01\x01\x12\x43\n\x05query\x18\x02 \x01(\x0b\x32-.xyz.block.ftl.timeline.v1.GetTimelineRequestR\x05queryB\x12\n\x10_update_interval\"R\n\x16StreamTimelineResponse\x12\x38\n\x06\x65vents\x18\x01 \x03(\x0b\x32 .xyz.block.ftl.timeline.v1.EventR\x06\x65vents\"\xfc\x05\n\x12\x43reateEventRequest\x12\x37\n\x03log\x18\x01 \x01(\x0b\x32#.xyz.block.ftl.timeline.v1.LogEventH\x00R\x03log\x12:\n\x04\x63\x61ll\x18\x02 \x01(\x0b\x32$.xyz.block.ftl.timeline.v1.CallEventH\x00R\x04\x63\x61ll\x12\x62\n\x12\x64\x65ployment_created\x18\x03 \x01(\x0b\x32\x31.xyz.block.ftl.timeline.v1.DeploymentCreatedEventH\x00R\x11\x64\x65ploymentCreated\x12\x62\n\x12\x64\x65ployment_updated\x18\x04 \x01(\x0b\x32\x31.xyz.block.ftl.timeline.v1.DeploymentUpdatedEventH\x00R\x11\x64\x65ploymentUpdated\x12\x43\n\x07ingress\x18\x05 \x01(\x0b\x32\'.xyz.block.ftl.timeline.v1.IngressEventH\x00R\x07ingress\x12V\n\x0e\x63ron_scheduled\x18\x06 \x01(\x0b\x32-.xyz.block.ftl.timeline.v1.CronScheduledEventH\x00R\rcronScheduled\x12S\n\rasync_execute\x18\x07 \x01(\x0b\x32,.xyz.block.ftl.timeline.v1.AsyncExecuteEventH\x00R\x0c\x61syncExecute\x12V\n\x0epubsub_publish\x18\x08 \x01(\x0b\x32-.xyz.block.ftl.timeline.v1.PubSubPublishEventH\x00R\rpubsubPublish\x12V\n\x0epubsub_consume\x18\t \x01(\x0b\x32-.xyz.block.ftl.timeline.v1.PubSubConsumeEventH\x00R\rpubsubConsumeB\x07\n\x05\x65ntry\"\x15\n\x13\x43reateEventResponse\"~\n\x16\x44\x65leteOldEventsRequest\x12\x43\n\nevent_type\x18\x01 \x01(\x0e\x32$.xyz.block.ftl.timeline.v1.EventTypeR\teventType\x12\x1f\n\x0b\x61ge_seconds\x18\x02 \x01(\x03R\nageSeconds\">\n\x17\x44\x65leteOldEventsResponse\x12#\n\rdeleted_count\x18\x01 \x01(\x03R\x0c\x64\x65letedCount2\xb5\x04\n\x0fTimelineService\x12J\n\x04Ping\x12\x1d.xyz.block.ftl.v1.PingRequest\x1a\x1e.xyz.block.ftl.v1.PingResponse\"\x03\x90\x02\x01\x12q\n\x0bGetTimeline\x12-.xyz.block.ftl.timeline.v1.GetTimelineRequest\x1a..xyz.block.ftl.timeline.v1.GetTimelineResponse\"\x03\x90\x02\x01\x12w\n\x0eStreamTimeline\x12\x30.xyz.block.ftl.timeline.v1.StreamTimelineRequest\x1a\x31.xyz.block.ftl.timeline.v1.StreamTimelineResponse0\x01\x12n\n\x0b\x43reateEvent\x12-.xyz.block.ftl.timeline.v1.CreateEventRequest\x1a..xyz.block.ftl.timeline.v1.CreateEventResponse\"\x00\x12z\n\x0f\x44\x65leteOldEvents\x12\x31.xyz.block.ftl.timeline.v1.DeleteOldEventsRequest\x1a\x32.xyz.block.ftl.timeline.v1.DeleteOldEventsResponse\"\x00\x42RP\x01ZNgithub.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/timeline/v1;timelinev1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(xyz/block/ftl/timeline/v1/timeline.proto\x12\x19xyz.block.ftl.timeline.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a%xyz/block/ftl/timeline/v1/event.proto\x1a\x1axyz/block/ftl/v1/ftl.proto\"\xf0\r\n\x12GetTimelineRequest\x12N\n\x07\x66ilters\x18\x01 \x03(\x0b\x32\x34.xyz.block.ftl.timeline.v1.GetTimelineRequest.FilterR\x07\x66ilters\x12\x14\n\x05limit\x18\x02 \x01(\x05R\x05limit\x12I\n\x05order\x18\x03 \x01(\x0e\x32\x33.xyz.block.ftl.timeline.v1.GetTimelineRequest.OrderR\x05order\x1aR\n\x0eLogLevelFilter\x12@\n\tlog_level\x18\x01 \x01(\x0e\x32#.xyz.block.ftl.timeline.v1.LogLevelR\x08logLevel\x1a\x34\n\x10\x44\x65ploymentFilter\x12 \n\x0b\x64\x65ployments\x18\x01 \x03(\tR\x0b\x64\x65ployments\x1a+\n\rRequestFilter\x12\x1a\n\x08requests\x18\x01 \x03(\tR\x08requests\x1aX\n\x0f\x45ventTypeFilter\x12\x45\n\x0b\x65vent_types\x18\x01 \x03(\x0e\x32$.xyz.block.ftl.timeline.v1.EventTypeR\neventTypes\x1a\xaa\x01\n\nTimeFilter\x12>\n\nolder_than\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x00R\tolderThan\x88\x01\x01\x12>\n\nnewer_than\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x01R\tnewerThan\x88\x01\x01\x42\r\n\x0b_older_thanB\r\n\x0b_newer_than\x1as\n\x08IDFilter\x12\"\n\nlower_than\x18\x01 \x01(\x03H\x00R\tlowerThan\x88\x01\x01\x12$\n\x0bhigher_than\x18\x02 \x01(\x03H\x01R\nhigherThan\x88\x01\x01\x42\r\n\x0b_lower_thanB\x0e\n\x0c_higher_than\x1a\x99\x01\n\nCallFilter\x12\x1f\n\x0b\x64\x65st_module\x18\x01 \x01(\tR\ndestModule\x12 \n\tdest_verb\x18\x02 \x01(\tH\x00R\x08\x64\x65stVerb\x88\x01\x01\x12(\n\rsource_module\x18\x03 \x01(\tH\x01R\x0csourceModule\x88\x01\x01\x42\x0c\n\n_dest_verbB\x10\n\x0e_source_module\x1aH\n\x0cModuleFilter\x12\x16\n\x06module\x18\x01 \x01(\tR\x06module\x12\x17\n\x04verb\x18\x02 \x01(\tH\x00R\x04verb\x88\x01\x01\x42\x07\n\x05_verb\x1a\xd0\x05\n\x06\x46ilter\x12[\n\tlog_level\x18\x01 \x01(\x0b\x32<.xyz.block.ftl.timeline.v1.GetTimelineRequest.LogLevelFilterH\x00R\x08logLevel\x12\x62\n\x0b\x64\x65ployments\x18\x02 \x01(\x0b\x32>.xyz.block.ftl.timeline.v1.GetTimelineRequest.DeploymentFilterH\x00R\x0b\x64\x65ployments\x12Y\n\x08requests\x18\x03 \x01(\x0b\x32;.xyz.block.ftl.timeline.v1.GetTimelineRequest.RequestFilterH\x00R\x08requests\x12`\n\x0b\x65vent_types\x18\x04 \x01(\x0b\x32=.xyz.block.ftl.timeline.v1.GetTimelineRequest.EventTypeFilterH\x00R\neventTypes\x12N\n\x04time\x18\x05 \x01(\x0b\x32\x38.xyz.block.ftl.timeline.v1.GetTimelineRequest.TimeFilterH\x00R\x04time\x12H\n\x02id\x18\x06 \x01(\x0b\x32\x36.xyz.block.ftl.timeline.v1.GetTimelineRequest.IDFilterH\x00R\x02id\x12N\n\x04\x63\x61ll\x18\x07 \x01(\x0b\x32\x38.xyz.block.ftl.timeline.v1.GetTimelineRequest.CallFilterH\x00R\x04\x63\x61ll\x12T\n\x06module\x18\x08 \x01(\x0b\x32:.xyz.block.ftl.timeline.v1.GetTimelineRequest.ModuleFilterH\x00R\x06moduleB\x08\n\x06\x66ilter\"=\n\x05Order\x12\x15\n\x11ORDER_UNSPECIFIED\x10\x00\x12\r\n\tORDER_ASC\x10\x01\x12\x0e\n\nORDER_DESC\x10\x02\"w\n\x13GetTimelineResponse\x12\x38\n\x06\x65vents\x18\x01 \x03(\x0b\x32 .xyz.block.ftl.timeline.v1.EventR\x06\x65vents\x12\x1b\n\x06\x63ursor\x18\x02 \x01(\x03H\x00R\x06\x63ursor\x88\x01\x01\x42\t\n\x07_cursor\"\xb9\x01\n\x15StreamTimelineRequest\x12G\n\x0fupdate_interval\x18\x01 \x01(\x0b\x32\x19.google.protobuf.DurationH\x00R\x0eupdateInterval\x88\x01\x01\x12\x43\n\x05query\x18\x02 \x01(\x0b\x32-.xyz.block.ftl.timeline.v1.GetTimelineRequestR\x05queryB\x12\n\x10_update_interval\"R\n\x16StreamTimelineResponse\x12\x38\n\x06\x65vents\x18\x01 \x03(\x0b\x32 .xyz.block.ftl.timeline.v1.EventR\x06\x65vents\"\x9b\x07\n\x13\x43reateEventsRequest\x12S\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\x39.xyz.block.ftl.timeline.v1.CreateEventsRequest.EventEntryR\x07\x65ntries\x1a\xae\x06\n\nEventEntry\x12\x38\n\ttimestamp\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\ttimestamp\x12\x37\n\x03log\x18\x02 \x01(\x0b\x32#.xyz.block.ftl.timeline.v1.LogEventH\x00R\x03log\x12:\n\x04\x63\x61ll\x18\x03 \x01(\x0b\x32$.xyz.block.ftl.timeline.v1.CallEventH\x00R\x04\x63\x61ll\x12\x62\n\x12\x64\x65ployment_created\x18\x04 \x01(\x0b\x32\x31.xyz.block.ftl.timeline.v1.DeploymentCreatedEventH\x00R\x11\x64\x65ploymentCreated\x12\x62\n\x12\x64\x65ployment_updated\x18\x05 \x01(\x0b\x32\x31.xyz.block.ftl.timeline.v1.DeploymentUpdatedEventH\x00R\x11\x64\x65ploymentUpdated\x12\x43\n\x07ingress\x18\x06 \x01(\x0b\x32\'.xyz.block.ftl.timeline.v1.IngressEventH\x00R\x07ingress\x12V\n\x0e\x63ron_scheduled\x18\x07 \x01(\x0b\x32-.xyz.block.ftl.timeline.v1.CronScheduledEventH\x00R\rcronScheduled\x12S\n\rasync_execute\x18\x08 \x01(\x0b\x32,.xyz.block.ftl.timeline.v1.AsyncExecuteEventH\x00R\x0c\x61syncExecute\x12V\n\x0epubsub_publish\x18\t \x01(\x0b\x32-.xyz.block.ftl.timeline.v1.PubSubPublishEventH\x00R\rpubsubPublish\x12V\n\x0epubsub_consume\x18\n \x01(\x0b\x32-.xyz.block.ftl.timeline.v1.PubSubConsumeEventH\x00R\rpubsubConsumeB\x07\n\x05\x65ntry\"\x16\n\x14\x43reateEventsResponse\"~\n\x16\x44\x65leteOldEventsRequest\x12\x43\n\nevent_type\x18\x01 \x01(\x0e\x32$.xyz.block.ftl.timeline.v1.EventTypeR\teventType\x12\x1f\n\x0b\x61ge_seconds\x18\x02 \x01(\x03R\nageSeconds\">\n\x17\x44\x65leteOldEventsResponse\x12#\n\rdeleted_count\x18\x01 \x01(\x03R\x0c\x64\x65letedCount2\xb8\x04\n\x0fTimelineService\x12J\n\x04Ping\x12\x1d.xyz.block.ftl.v1.PingRequest\x1a\x1e.xyz.block.ftl.v1.PingResponse\"\x03\x90\x02\x01\x12q\n\x0bGetTimeline\x12-.xyz.block.ftl.timeline.v1.GetTimelineRequest\x1a..xyz.block.ftl.timeline.v1.GetTimelineResponse\"\x03\x90\x02\x01\x12w\n\x0eStreamTimeline\x12\x30.xyz.block.ftl.timeline.v1.StreamTimelineRequest\x1a\x31.xyz.block.ftl.timeline.v1.StreamTimelineResponse0\x01\x12q\n\x0c\x43reateEvents\x12..xyz.block.ftl.timeline.v1.CreateEventsRequest\x1a/.xyz.block.ftl.timeline.v1.CreateEventsResponse\"\x00\x12z\n\x0f\x44\x65leteOldEvents\x12\x31.xyz.block.ftl.timeline.v1.DeleteOldEventsRequest\x1a\x32.xyz.block.ftl.timeline.v1.DeleteOldEventsResponse\"\x00\x42RP\x01ZNgithub.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/timeline/v1;timelinev1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -68,14 +68,16 @@ _globals['_STREAMTIMELINEREQUEST']._serialized_end=2289 _globals['_STREAMTIMELINERESPONSE']._serialized_start=2291 _globals['_STREAMTIMELINERESPONSE']._serialized_end=2373 - _globals['_CREATEEVENTREQUEST']._serialized_start=2376 - _globals['_CREATEEVENTREQUEST']._serialized_end=3140 - _globals['_CREATEEVENTRESPONSE']._serialized_start=3142 - _globals['_CREATEEVENTRESPONSE']._serialized_end=3163 - _globals['_DELETEOLDEVENTSREQUEST']._serialized_start=3165 - _globals['_DELETEOLDEVENTSREQUEST']._serialized_end=3291 - _globals['_DELETEOLDEVENTSRESPONSE']._serialized_start=3293 - _globals['_DELETEOLDEVENTSRESPONSE']._serialized_end=3355 - _globals['_TIMELINESERVICE']._serialized_start=3358 - _globals['_TIMELINESERVICE']._serialized_end=3923 + _globals['_CREATEEVENTSREQUEST']._serialized_start=2376 + _globals['_CREATEEVENTSREQUEST']._serialized_end=3299 + _globals['_CREATEEVENTSREQUEST_EVENTENTRY']._serialized_start=2485 + _globals['_CREATEEVENTSREQUEST_EVENTENTRY']._serialized_end=3299 + _globals['_CREATEEVENTSRESPONSE']._serialized_start=3301 + _globals['_CREATEEVENTSRESPONSE']._serialized_end=3323 + _globals['_DELETEOLDEVENTSREQUEST']._serialized_start=3325 + _globals['_DELETEOLDEVENTSREQUEST']._serialized_end=3451 + _globals['_DELETEOLDEVENTSRESPONSE']._serialized_start=3453 + _globals['_DELETEOLDEVENTSRESPONSE']._serialized_end=3515 + _globals['_TIMELINESERVICE']._serialized_start=3518 + _globals['_TIMELINESERVICE']._serialized_end=4086 # @@protoc_insertion_point(module_scope) diff --git a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/timeline/v1/timeline_pb2.pyi b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/timeline/v1/timeline_pb2.pyi index f77b03cecb..ea6010fbed 100644 --- a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/timeline/v1/timeline_pb2.pyi +++ b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/timeline/v1/timeline_pb2.pyi @@ -119,29 +119,36 @@ class StreamTimelineResponse(_message.Message): events: _containers.RepeatedCompositeFieldContainer[_event_pb2.Event] def __init__(self, events: _Optional[_Iterable[_Union[_event_pb2.Event, _Mapping]]] = ...) -> None: ... -class CreateEventRequest(_message.Message): - __slots__ = ("log", "call", "deployment_created", "deployment_updated", "ingress", "cron_scheduled", "async_execute", "pubsub_publish", "pubsub_consume") - LOG_FIELD_NUMBER: _ClassVar[int] - CALL_FIELD_NUMBER: _ClassVar[int] - DEPLOYMENT_CREATED_FIELD_NUMBER: _ClassVar[int] - DEPLOYMENT_UPDATED_FIELD_NUMBER: _ClassVar[int] - INGRESS_FIELD_NUMBER: _ClassVar[int] - CRON_SCHEDULED_FIELD_NUMBER: _ClassVar[int] - ASYNC_EXECUTE_FIELD_NUMBER: _ClassVar[int] - PUBSUB_PUBLISH_FIELD_NUMBER: _ClassVar[int] - PUBSUB_CONSUME_FIELD_NUMBER: _ClassVar[int] - log: _event_pb2.LogEvent - call: _event_pb2.CallEvent - deployment_created: _event_pb2.DeploymentCreatedEvent - deployment_updated: _event_pb2.DeploymentUpdatedEvent - ingress: _event_pb2.IngressEvent - cron_scheduled: _event_pb2.CronScheduledEvent - async_execute: _event_pb2.AsyncExecuteEvent - pubsub_publish: _event_pb2.PubSubPublishEvent - pubsub_consume: _event_pb2.PubSubConsumeEvent - def __init__(self, log: _Optional[_Union[_event_pb2.LogEvent, _Mapping]] = ..., call: _Optional[_Union[_event_pb2.CallEvent, _Mapping]] = ..., deployment_created: _Optional[_Union[_event_pb2.DeploymentCreatedEvent, _Mapping]] = ..., deployment_updated: _Optional[_Union[_event_pb2.DeploymentUpdatedEvent, _Mapping]] = ..., ingress: _Optional[_Union[_event_pb2.IngressEvent, _Mapping]] = ..., cron_scheduled: _Optional[_Union[_event_pb2.CronScheduledEvent, _Mapping]] = ..., async_execute: _Optional[_Union[_event_pb2.AsyncExecuteEvent, _Mapping]] = ..., pubsub_publish: _Optional[_Union[_event_pb2.PubSubPublishEvent, _Mapping]] = ..., pubsub_consume: _Optional[_Union[_event_pb2.PubSubConsumeEvent, _Mapping]] = ...) -> None: ... +class CreateEventsRequest(_message.Message): + __slots__ = ("entries",) + class EventEntry(_message.Message): + __slots__ = ("timestamp", "log", "call", "deployment_created", "deployment_updated", "ingress", "cron_scheduled", "async_execute", "pubsub_publish", "pubsub_consume") + TIMESTAMP_FIELD_NUMBER: _ClassVar[int] + LOG_FIELD_NUMBER: _ClassVar[int] + CALL_FIELD_NUMBER: _ClassVar[int] + DEPLOYMENT_CREATED_FIELD_NUMBER: _ClassVar[int] + DEPLOYMENT_UPDATED_FIELD_NUMBER: _ClassVar[int] + INGRESS_FIELD_NUMBER: _ClassVar[int] + CRON_SCHEDULED_FIELD_NUMBER: _ClassVar[int] + ASYNC_EXECUTE_FIELD_NUMBER: _ClassVar[int] + PUBSUB_PUBLISH_FIELD_NUMBER: _ClassVar[int] + PUBSUB_CONSUME_FIELD_NUMBER: _ClassVar[int] + timestamp: _timestamp_pb2.Timestamp + log: _event_pb2.LogEvent + call: _event_pb2.CallEvent + deployment_created: _event_pb2.DeploymentCreatedEvent + deployment_updated: _event_pb2.DeploymentUpdatedEvent + ingress: _event_pb2.IngressEvent + cron_scheduled: _event_pb2.CronScheduledEvent + async_execute: _event_pb2.AsyncExecuteEvent + pubsub_publish: _event_pb2.PubSubPublishEvent + pubsub_consume: _event_pb2.PubSubConsumeEvent + def __init__(self, timestamp: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., log: _Optional[_Union[_event_pb2.LogEvent, _Mapping]] = ..., call: _Optional[_Union[_event_pb2.CallEvent, _Mapping]] = ..., deployment_created: _Optional[_Union[_event_pb2.DeploymentCreatedEvent, _Mapping]] = ..., deployment_updated: _Optional[_Union[_event_pb2.DeploymentUpdatedEvent, _Mapping]] = ..., ingress: _Optional[_Union[_event_pb2.IngressEvent, _Mapping]] = ..., cron_scheduled: _Optional[_Union[_event_pb2.CronScheduledEvent, _Mapping]] = ..., async_execute: _Optional[_Union[_event_pb2.AsyncExecuteEvent, _Mapping]] = ..., pubsub_publish: _Optional[_Union[_event_pb2.PubSubPublishEvent, _Mapping]] = ..., pubsub_consume: _Optional[_Union[_event_pb2.PubSubConsumeEvent, _Mapping]] = ...) -> None: ... + ENTRIES_FIELD_NUMBER: _ClassVar[int] + entries: _containers.RepeatedCompositeFieldContainer[CreateEventsRequest.EventEntry] + def __init__(self, entries: _Optional[_Iterable[_Union[CreateEventsRequest.EventEntry, _Mapping]]] = ...) -> None: ... -class CreateEventResponse(_message.Message): +class CreateEventsResponse(_message.Message): __slots__ = () def __init__(self) -> None: ...