diff --git a/api/client/client.go b/api/client/client.go index a8d7f17f671..5519f39df8e 100644 --- a/api/client/client.go +++ b/api/client/client.go @@ -76,6 +76,7 @@ import ( kubewaitingcontainerpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/kubewaitingcontainer/v1" loginrulepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/loginrule/v1" machineidv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/machineid/v1" + notificationsv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/notifications/v1" oktapb "github.com/gravitational/teleport/api/gen/proto/go/teleport/okta/v1" pluginspb "github.com/gravitational/teleport/api/gen/proto/go/teleport/plugins/v1" presencepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/presence/v1" @@ -111,6 +112,7 @@ type AuthServiceClient struct { assist.AssistServiceClient auditlogpb.AuditLogServiceClient userpreferencespb.UserPreferencesServiceClient + notificationsv1pb.NotificationServiceClient } // Client is a gRPC Client that connects to a Teleport Auth server either @@ -522,6 +524,7 @@ func (c *Client) dialGRPC(ctx context.Context, addr string) error { AssistServiceClient: assist.NewAssistServiceClient(c.conn), AuditLogServiceClient: auditlogpb.NewAuditLogServiceClient(c.conn), UserPreferencesServiceClient: userpreferencespb.NewUserPreferencesServiceClient(c.conn), + NotificationServiceClient: notificationsv1pb.NewNotificationServiceClient(c.conn), } c.JoinServiceClient = NewJoinServiceClient(proto.NewJoinServiceClient(c.conn)) @@ -865,6 +868,11 @@ func (c *Client) WorkloadIdentityServiceClient() machineidv1pb.WorkloadIdentityS return machineidv1pb.NewWorkloadIdentityServiceClient(c.conn) } +// NotificationServiceClient returns a notification service client that can be used to fetch notifications. +func (c *Client) NotificationServiceClient() notificationsv1pb.NotificationServiceClient { + return notificationsv1pb.NewNotificationServiceClient(c.conn) +} + // Ping gets basic info about the auth server. func (c *Client) Ping(ctx context.Context) (proto.PingResponse, error) { rsp, err := c.grpc.Ping(ctx, &proto.PingRequest{}) @@ -4899,6 +4907,25 @@ func (c *Client) UpsertUserPreferences(ctx context.Context, in *userpreferencesp return nil } +// ListNotifications returns a paginated list of notifications for the user. +// This includes global notifications which match the user, as well as user-specific notifications for the user. +func (c *Client) ListNotifications(ctx context.Context, req *notificationsv1pb.ListNotificationsRequest) (*notificationsv1pb.ListNotificationsResponse, error) { + rsp, err := c.NotificationServiceClient().ListNotifications(ctx, req) + return rsp, trace.Wrap(err) +} + +// UpsertUserNotificationState creates or updates a user notification state which records whether the user has clicked on or dismissed a notification. +func (c *Client) UpsertUserNotificationState(ctx context.Context, req *notificationsv1pb.UpsertUserNotificationStateRequest) (*notificationsv1pb.UserNotificationState, error) { + rsp, err := c.NotificationServiceClient().UpsertUserNotificationState(ctx, req) + return rsp, trace.Wrap(err) +} + +// UpsertUserLastSeenNotification creates or updates a user's last seen notification timestamp. +func (c *Client) UpsertUserLastSeenNotification(ctx context.Context, req *notificationsv1pb.UpsertUserLastSeenNotificationRequest) (*notificationsv1pb.UserLastSeenNotification, error) { + rsp, err := c.NotificationServiceClient().UpsertUserLastSeenNotification(ctx, req) + return rsp, trace.Wrap(err) +} + // ResourceUsageClient returns an unadorned Resource Usage service client, // using the underlying Auth gRPC connection. // Clients connecting to non-Enterprise clusters, or older Teleport versions, diff --git a/api/gen/proto/go/teleport/notifications/v1/notifications.pb.go b/api/gen/proto/go/teleport/notifications/v1/notifications.pb.go index 5cd61d6808c..bff1add294c 100644 --- a/api/gen/proto/go/teleport/notifications/v1/notifications.pb.go +++ b/api/gen/proto/go/teleport/notifications/v1/notifications.pb.go @@ -183,8 +183,6 @@ type NotificationSpec struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // id is the UUID of the notification, in UUIDv7 format. - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // created is when the notification was created, in UNIX time. Created *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=created,proto3" json:"created,omitempty"` // unscoped is whether the notification shouldn't be restricted to a specific audience. This is to prevent the potential future possibility that a user-specific notification contains information that the user should no longer be allowed to see. Default is true. @@ -225,13 +223,6 @@ func (*NotificationSpec) Descriptor() ([]byte, []int) { return file_teleport_notifications_v1_notifications_proto_rawDescGZIP(), []int{1} } -func (x *NotificationSpec) GetId() string { - if x != nil { - return x.Id - } - return "" -} - func (x *NotificationSpec) GetCreated() *timestamppb.Timestamp { if x != nil { return x.Created @@ -960,133 +951,132 @@ var file_teleport_notifications_v1_notifications_proto_rawDesc = []byte{ 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, - 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x22, 0x90, 0x01, 0x0a, 0x10, 0x4e, 0x6f, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x34, 0x0a, 0x07, 0x63, + 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x22, 0x8a, 0x01, 0x0a, 0x10, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 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, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x6e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x75, 0x6e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xde, 0x01, 0x0a, 0x12, 0x47, 0x6c, - 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x5f, 0x6b, 0x69, 0x6e, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x4b, 0x69, 0x6e, 0x64, 0x12, - 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x31, - 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x12, 0x45, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x6c, - 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x22, 0xca, 0x02, 0x0a, 0x16, 0x47, - 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x53, 0x70, 0x65, 0x63, 0x12, 0x51, 0x0a, 0x0e, 0x62, 0x79, 0x5f, 0x70, 0x65, 0x72, 0x6d, - 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, - 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x79, 0x50, 0x65, 0x72, 0x6d, - 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x48, 0x00, 0x52, 0x0d, 0x62, 0x79, 0x50, 0x65, 0x72, - 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3f, 0x0a, 0x08, 0x62, 0x79, 0x5f, 0x72, - 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x79, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x48, 0x00, - 0x52, 0x07, 0x62, 0x79, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x03, 0x61, 0x6c, 0x6c, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x30, 0x0a, - 0x14, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x6d, 0x61, 0x74, - 0x63, 0x68, 0x41, 0x6c, 0x6c, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, - 0x4b, 0x0a, 0x0c, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, - 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, - 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x22, 0x4f, 0x0a, 0x0d, 0x42, 0x79, 0x50, 0x65, 0x72, - 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3e, 0x0a, 0x0f, 0x72, 0x6f, 0x6c, 0x65, - 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x43, 0x6f, - 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0e, 0x72, 0x6f, 0x6c, 0x65, 0x43, 0x6f, - 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x1f, 0x0a, 0x07, 0x42, 0x79, 0x52, 0x6f, - 0x6c, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x22, 0xb4, 0x02, 0x0a, 0x15, 0x55, 0x73, - 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x5f, 0x6b, - 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x4b, 0x69, - 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x08, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x48, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x52, + 0x02, 0x69, 0x64, 0x22, 0xde, 0x01, 0x0a, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, + 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x19, + 0x0a, 0x08, 0x73, 0x75, 0x62, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x73, 0x75, 0x62, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x45, 0x0a, + 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, + 0x73, 0x70, 0x65, 0x63, 0x22, 0xca, 0x02, 0x0a, 0x16, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4e, + 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x12, + 0x51, 0x0a, 0x0e, 0x62, 0x79, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x79, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x48, 0x00, 0x52, 0x0d, 0x62, 0x79, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x3f, 0x0a, 0x08, 0x62, 0x79, 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, - 0x12, 0x4e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x36, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, - 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x22, 0x44, 0x0a, 0x19, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x27, 0x0a, - 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x7a, 0x0a, 0x1b, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x5b, 0x0a, 0x12, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, - 0x11, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x22, 0xc9, 0x02, 0x0a, 0x18, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, - 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, - 0x69, 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x18, - 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x12, 0x4b, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x37, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, - 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, - 0x51, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x39, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, + 0x2e, 0x42, 0x79, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x48, 0x00, 0x52, 0x07, 0x62, 0x79, 0x52, 0x6f, + 0x6c, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x48, 0x00, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x30, 0x0a, 0x14, 0x6d, 0x61, 0x74, 0x63, 0x68, + 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x41, 0x6c, 0x6c, 0x43, + 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x4b, 0x0a, 0x0c, 0x6e, 0x6f, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, + 0x72, 0x22, 0x4f, 0x0a, 0x0d, 0x42, 0x79, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x3e, 0x0a, 0x0f, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x52, 0x0e, 0x72, 0x6f, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x22, 0x1f, 0x0a, 0x07, 0x42, 0x79, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x14, 0x0a, + 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x72, 0x6f, + 0x6c, 0x65, 0x73, 0x22, 0xb4, 0x02, 0x0a, 0x15, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, + 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x12, 0x48, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4e, + 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x4e, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x44, 0x0a, 0x19, 0x55, 0x73, + 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x27, 0x0a, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, + 0x22, 0x7a, 0x0a, 0x1b, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x5b, 0x0a, 0x12, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x11, 0x6e, 0x6f, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0xc9, 0x02, 0x0a, + 0x18, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x19, 0x0a, + 0x08, 0x73, 0x75, 0x62, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x73, 0x75, 0x62, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4b, 0x0a, 0x04, + 0x73, 0x70, 0x65, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, + 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, + 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x51, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, + 0x65, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x04, 0x08, 0x06, + 0x10, 0x07, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x1e, 0x0a, 0x1c, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x1e, - 0x0a, 0x1c, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x22, 0x62, - 0x0a, 0x1e, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x40, 0x0a, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 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, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x54, 0x69, - 0x6d, 0x65, 0x2a, 0x79, 0x0a, 0x11, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x22, 0x0a, 0x1e, 0x4e, 0x4f, 0x54, 0x49, 0x46, - 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, - 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x4e, - 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, - 0x45, 0x5f, 0x43, 0x4c, 0x49, 0x43, 0x4b, 0x45, 0x44, 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x4e, - 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, - 0x45, 0x5f, 0x44, 0x49, 0x53, 0x4d, 0x49, 0x53, 0x53, 0x45, 0x44, 0x10, 0x02, 0x42, 0x5e, 0x5a, - 0x5c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, - 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x6e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x76, 0x31, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x22, 0x62, 0x0a, 0x1e, 0x55, 0x73, 0x65, 0x72, + 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x40, 0x0a, 0x0e, 0x6c, 0x61, + 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 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, 0x0c, + 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x2a, 0x79, 0x0a, 0x11, + 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x22, 0x0a, 0x1e, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, + 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, + 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, + 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x43, 0x4c, 0x49, 0x43, + 0x4b, 0x45, 0x44, 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, + 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x49, 0x53, 0x4d, + 0x49, 0x53, 0x53, 0x45, 0x44, 0x10, 0x02, 0x42, 0x5e, 0x5a, 0x5c, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/api/gen/proto/go/teleport/notifications/v1/notifications_service.pb.go b/api/gen/proto/go/teleport/notifications/v1/notifications_service.pb.go index 452bc49069a..af5b92c2b3d 100644 --- a/api/gen/proto/go/teleport/notifications/v1/notifications_service.pb.go +++ b/api/gen/proto/go/teleport/notifications/v1/notifications_service.pb.go @@ -27,6 +27,7 @@ import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" emptypb "google.golang.org/protobuf/types/known/emptypb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" ) @@ -154,22 +155,20 @@ func (x *DeleteUserNotificationRequest) GetNotificationId() string { return "" } -// ListUserNotificationsRequest is the request for listing a user's notifications, which include user-specific ones as well as global notifications that match them. -type ListUserNotificationsRequest struct { +// ListNotificationsRequest is the request for listing a user's notifications. +type ListNotificationsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // username is the username of the user the notifications to list are for. - Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` // page_size is the size of the page to return. - PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + PageSize int32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` // page_token is the next_page_token value returned from a previous ListUserNotifications request, if any. - PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` + PageToken string `protobuf:"bytes,2,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` } -func (x *ListUserNotificationsRequest) Reset() { - *x = ListUserNotificationsRequest{} +func (x *ListNotificationsRequest) Reset() { + *x = ListNotificationsRequest{} if protoimpl.UnsafeEnabled { mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -177,13 +176,13 @@ func (x *ListUserNotificationsRequest) Reset() { } } -func (x *ListUserNotificationsRequest) String() string { +func (x *ListNotificationsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListUserNotificationsRequest) ProtoMessage() {} +func (*ListNotificationsRequest) ProtoMessage() {} -func (x *ListUserNotificationsRequest) ProtoReflect() protoreflect.Message { +func (x *ListNotificationsRequest) ProtoReflect() protoreflect.Message { mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -195,46 +194,41 @@ func (x *ListUserNotificationsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListUserNotificationsRequest.ProtoReflect.Descriptor instead. -func (*ListUserNotificationsRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use ListNotificationsRequest.ProtoReflect.Descriptor instead. +func (*ListNotificationsRequest) Descriptor() ([]byte, []int) { return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{2} } -func (x *ListUserNotificationsRequest) GetUsername() string { - if x != nil { - return x.Username - } - return "" -} - -func (x *ListUserNotificationsRequest) GetPageSize() int32 { +func (x *ListNotificationsRequest) GetPageSize() int32 { if x != nil { return x.PageSize } return 0 } -func (x *ListUserNotificationsRequest) GetPageToken() string { +func (x *ListNotificationsRequest) GetPageToken() string { if x != nil { return x.PageToken } return "" } -// ListUserNotificationsResponse is the response from listing a user's user-specific notifications. -type ListUserNotificationsResponse struct { +// ListNotificationsResponse is the response from listing a user's notifications. +type ListNotificationsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // notifications is the notifications returned. Notifications []*Notification `protobuf:"bytes,1,rep,name=notifications,proto3" json:"notifications,omitempty"` - // next_page_token is the token to retrieve the next page of results, this will be empty if there are no more results. - NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + // next_page_token contains the next page token to use as the start key for the next page of notifications. + NextPageToken string `protobuf:"bytes,3,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + // user_last_seen_notification_timestamp is the timestamp of the last notification the user has seen. + UserLastSeenNotificationTimestamp *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=user_last_seen_notification_timestamp,json=userLastSeenNotificationTimestamp,proto3" json:"user_last_seen_notification_timestamp,omitempty"` } -func (x *ListUserNotificationsResponse) Reset() { - *x = ListUserNotificationsResponse{} +func (x *ListNotificationsResponse) Reset() { + *x = ListNotificationsResponse{} if protoimpl.UnsafeEnabled { mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -242,13 +236,13 @@ func (x *ListUserNotificationsResponse) Reset() { } } -func (x *ListUserNotificationsResponse) String() string { +func (x *ListNotificationsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListUserNotificationsResponse) ProtoMessage() {} +func (*ListNotificationsResponse) ProtoMessage() {} -func (x *ListUserNotificationsResponse) ProtoReflect() protoreflect.Message { +func (x *ListNotificationsResponse) ProtoReflect() protoreflect.Message { mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -260,25 +254,32 @@ func (x *ListUserNotificationsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListUserNotificationsResponse.ProtoReflect.Descriptor instead. -func (*ListUserNotificationsResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use ListNotificationsResponse.ProtoReflect.Descriptor instead. +func (*ListNotificationsResponse) Descriptor() ([]byte, []int) { return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{3} } -func (x *ListUserNotificationsResponse) GetNotifications() []*Notification { +func (x *ListNotificationsResponse) GetNotifications() []*Notification { if x != nil { return x.Notifications } return nil } -func (x *ListUserNotificationsResponse) GetNextPageToken() string { +func (x *ListNotificationsResponse) GetNextPageToken() string { if x != nil { return x.NextPageToken } return "" } +func (x *ListNotificationsResponse) GetUserLastSeenNotificationTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.UserLastSeenNotificationTimestamp + } + return nil +} + // CreateGlobalNotificationRequest is the request for creating a global notification. type CreateGlobalNotificationRequest struct { state protoimpl.MessageState @@ -435,278 +436,6 @@ func (x *UpsertUserNotificationStateRequest) GetUserNotificationState() *UserNot return nil } -// UpdateUserNotificationStateRequest is the request for updating a user notification state. -type UpdateUserNotificationStateRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // username is the username of the user. - Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` - // user_notification_state is the updated user notification state. - UserNotificationState *UserNotificationState `protobuf:"bytes,2,opt,name=user_notification_state,json=userNotificationState,proto3" json:"user_notification_state,omitempty"` -} - -func (x *UpdateUserNotificationStateRequest) Reset() { - *x = UpdateUserNotificationStateRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *UpdateUserNotificationStateRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UpdateUserNotificationStateRequest) ProtoMessage() {} - -func (x *UpdateUserNotificationStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UpdateUserNotificationStateRequest.ProtoReflect.Descriptor instead. -func (*UpdateUserNotificationStateRequest) Descriptor() ([]byte, []int) { - return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{7} -} - -func (x *UpdateUserNotificationStateRequest) GetUsername() string { - if x != nil { - return x.Username - } - return "" -} - -func (x *UpdateUserNotificationStateRequest) GetUserNotificationState() *UserNotificationState { - if x != nil { - return x.UserNotificationState - } - return nil -} - -// DeleteUserNotificationState is the request for deleting a user notification state. -type DeleteUserNotificationStateRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // username is the username of the user. - Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` - // notification_id is the ID of the notification the notifiation state to delete is for. - NotificationId string `protobuf:"bytes,2,opt,name=notification_id,json=notificationId,proto3" json:"notification_id,omitempty"` -} - -func (x *DeleteUserNotificationStateRequest) Reset() { - *x = DeleteUserNotificationStateRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DeleteUserNotificationStateRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteUserNotificationStateRequest) ProtoMessage() {} - -func (x *DeleteUserNotificationStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteUserNotificationStateRequest.ProtoReflect.Descriptor instead. -func (*DeleteUserNotificationStateRequest) Descriptor() ([]byte, []int) { - return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{8} -} - -func (x *DeleteUserNotificationStateRequest) GetUsername() string { - if x != nil { - return x.Username - } - return "" -} - -func (x *DeleteUserNotificationStateRequest) GetNotificationId() string { - if x != nil { - return x.NotificationId - } - return "" -} - -// ListUserNotificationStatesRequest is the request for listing a user's notification states. -type ListUserNotificationStatesRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // username is the username of the user. - Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` -} - -func (x *ListUserNotificationStatesRequest) Reset() { - *x = ListUserNotificationStatesRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ListUserNotificationStatesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListUserNotificationStatesRequest) ProtoMessage() {} - -func (x *ListUserNotificationStatesRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListUserNotificationStatesRequest.ProtoReflect.Descriptor instead. -func (*ListUserNotificationStatesRequest) Descriptor() ([]byte, []int) { - return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{9} -} - -func (x *ListUserNotificationStatesRequest) GetUsername() string { - if x != nil { - return x.Username - } - return "" -} - -// ListUserNotificationStatesResponse is the response from listing a user's notification states. -type ListUserNotificationStatesResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // notification_states is the user notification state items returned. - NotificationStates []*UserNotificationState `protobuf:"bytes,1,rep,name=notification_states,json=notificationStates,proto3" json:"notification_states,omitempty"` - // next_page_token is the token to retrieve the next page of results, this will be empty if there are no more results. - NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` -} - -func (x *ListUserNotificationStatesResponse) Reset() { - *x = ListUserNotificationStatesResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ListUserNotificationStatesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListUserNotificationStatesResponse) ProtoMessage() {} - -func (x *ListUserNotificationStatesResponse) ProtoReflect() protoreflect.Message { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListUserNotificationStatesResponse.ProtoReflect.Descriptor instead. -func (*ListUserNotificationStatesResponse) Descriptor() ([]byte, []int) { - return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{10} -} - -func (x *ListUserNotificationStatesResponse) GetNotificationStates() []*UserNotificationState { - if x != nil { - return x.NotificationStates - } - return nil -} - -func (x *ListUserNotificationStatesResponse) GetNextPageToken() string { - if x != nil { - return x.NextPageToken - } - return "" -} - -// GetUserLastSeenNotificationRequest is the request for getting a user's last seen notification. -type GetUserLastSeenNotificationRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // username is the username of the user. - Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` -} - -func (x *GetUserLastSeenNotificationRequest) Reset() { - *x = GetUserLastSeenNotificationRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetUserLastSeenNotificationRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetUserLastSeenNotificationRequest) ProtoMessage() {} - -func (x *GetUserLastSeenNotificationRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetUserLastSeenNotificationRequest.ProtoReflect.Descriptor instead. -func (*GetUserLastSeenNotificationRequest) Descriptor() ([]byte, []int) { - return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{11} -} - -func (x *GetUserLastSeenNotificationRequest) GetUsername() string { - if x != nil { - return x.Username - } - return "" -} - // UpsertUserLastSeenNotificationRequest is the request for creating or updating a user's last seen notification. type UpsertUserLastSeenNotificationRequest struct { state protoimpl.MessageState @@ -715,14 +444,14 @@ type UpsertUserLastSeenNotificationRequest struct { // username is the username of the user. Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` - // user_notification_state is the udpated user last seen notification item. + // user_notification_state is the updated user last seen notification item. UserLastSeenNotification *UserLastSeenNotification `protobuf:"bytes,2,opt,name=user_last_seen_notification,json=userLastSeenNotification,proto3" json:"user_last_seen_notification,omitempty"` } func (x *UpsertUserLastSeenNotificationRequest) Reset() { *x = UpsertUserLastSeenNotificationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[12] + mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -735,7 +464,7 @@ func (x *UpsertUserLastSeenNotificationRequest) String() string { func (*UpsertUserLastSeenNotificationRequest) ProtoMessage() {} func (x *UpsertUserLastSeenNotificationRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[12] + mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -748,7 +477,7 @@ func (x *UpsertUserLastSeenNotificationRequest) ProtoReflect() protoreflect.Mess // Deprecated: Use UpsertUserLastSeenNotificationRequest.ProtoReflect.Descriptor instead. func (*UpsertUserLastSeenNotificationRequest) Descriptor() ([]byte, []int) { - return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{12} + return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{7} } func (x *UpsertUserLastSeenNotificationRequest) GetUsername() string { @@ -765,55 +494,6 @@ func (x *UpsertUserLastSeenNotificationRequest) GetUserLastSeenNotification() *U return nil } -// DeleteUserLastSeenNotificationRequest is the request for deleting a user's last seen notification. -type DeleteUserLastSeenNotificationRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // username is the username of the user. - Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` -} - -func (x *DeleteUserLastSeenNotificationRequest) Reset() { - *x = DeleteUserLastSeenNotificationRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DeleteUserLastSeenNotificationRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteUserLastSeenNotificationRequest) ProtoMessage() {} - -func (x *DeleteUserLastSeenNotificationRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[13] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteUserLastSeenNotificationRequest.ProtoReflect.Descriptor instead. -func (*DeleteUserLastSeenNotificationRequest) Descriptor() ([]byte, []int) { - return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{13} -} - -func (x *DeleteUserLastSeenNotificationRequest) GetUsername() string { - if x != nil { - return x.Username - } - return "" -} - var File_teleport_notifications_v1_notifications_service_proto protoreflect.FileDescriptor var file_teleport_notifications_v1_notifications_service_proto_rawDesc = []byte{ @@ -824,218 +504,148 @@ var file_teleport_notifications_v1_notifications_service_proto_rawDesc = []byte{ 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, - 0x2d, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x88, - 0x01, 0x0a, 0x1d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0c, - 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4e, - 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x64, 0x0a, 0x1d, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, - 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, - 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, - 0x76, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, - 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, - 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, - 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, - 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x96, 0x01, 0x0a, 0x1d, 0x4c, 0x69, 0x73, 0x74, - 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0d, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, - 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x22, 0x81, 0x01, 0x0a, 0x1f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, - 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x5e, 0x0a, 0x13, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x6e, - 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x6c, - 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x12, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x4a, 0x0a, 0x1f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x47, 0x6c, - 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, - 0x22, 0xaa, 0x01, 0x0a, 0x22, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4e, - 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x68, 0x0a, 0x17, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6e, 0x6f, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, - 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x15, 0x75, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0xaa, 0x01, - 0x0a, 0x22, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x68, 0x0a, 0x17, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x30, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, - 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x52, 0x15, 0x75, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x69, 0x0a, 0x22, 0x44, 0x65, + 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x2d, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6e, 0x6f, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, + 0x88, 0x01, 0x0a, 0x1d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x4b, 0x0a, + 0x0c, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, + 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x6e, 0x6f, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x64, 0x0a, 0x1d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, - 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x3f, 0x0a, 0x21, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, - 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, - 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, - 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xaf, 0x01, 0x0a, 0x22, 0x4c, 0x69, 0x73, 0x74, 0x55, - 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, - 0x13, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x12, 0x6e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, - 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, - 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, - 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x40, 0x0a, 0x22, 0x47, 0x65, 0x74, 0x55, - 0x73, 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, - 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xb7, 0x01, 0x0a, 0x25, 0x55, - 0x70, 0x73, 0x65, 0x72, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, - 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x72, 0x0a, 0x1b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, - 0x65, 0x6e, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x18, 0x75, 0x73, 0x65, 0x72, - 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x43, 0x0a, 0x25, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, - 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x32, 0xd0, 0x0b, 0x0a, 0x13, 0x4e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x12, 0x7b, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x4e, - 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, - 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x6a, - 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x4e, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, + 0x22, 0x56, 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, + 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, + 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, + 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x80, 0x02, 0x0a, 0x19, 0x4c, 0x69, 0x73, + 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, + 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x6c, 0x0a, + 0x25, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, + 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 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, 0x21, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x61, + 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x81, 0x01, 0x0a, 0x1f, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x5e, 0x0a, 0x13, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4e, + 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x12, 0x67, 0x6c, 0x6f, + 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, + 0x4a, 0x0a, 0x1f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x85, 0x01, 0x0a, 0x18, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x6f, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0xaa, 0x01, 0x0a, 0x22, + 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x68, + 0x0a, 0x17, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x30, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, + 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x52, 0x15, 0x75, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0xb7, 0x01, 0x0a, 0x25, 0x55, 0x70, 0x73, + 0x65, 0x72, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, + 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x72, + 0x0a, 0x1b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, + 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, + 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x18, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x61, + 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x32, 0xa1, 0x07, 0x0a, 0x13, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7b, 0x0a, 0x16, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x6a, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x38, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x12, 0x85, 0x01, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x6c, + 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x3a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4e, + 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x6e, 0x0a, 0x18, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, - 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x6e, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x47, 0x6c, 0x6f, 0x62, - 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3a, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x12, 0x8a, 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4e, - 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x37, 0x2e, 0x74, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, - 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x7e, 0x0a, 0x11, 0x4c, + 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x33, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x8e, 0x01, 0x0a, 0x1b, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, - 0x3d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, + 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x8e, 0x01, 0x0a, 0x1b, + 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3d, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x55, 0x73, + 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x97, 0x01, 0x0a, + 0x1e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, + 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x40, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, - 0x72, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4e, - 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x12, 0x74, 0x0a, 0x1b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, - 0x3d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x99, 0x01, 0x0a, 0x1a, 0x4c, 0x69, 0x73, 0x74, 0x55, - 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x73, 0x12, 0x3c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x3d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, - 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x91, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, - 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x3d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, + 0x72, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x97, 0x01, 0x0a, 0x1e, 0x55, 0x70, 0x73, 0x65, 0x72, - 0x74, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x55, 0x73, 0x65, 0x72, - 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, - 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x7a, 0x0a, 0x1e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, - 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x40, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, - 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x5e, 0x5a, 0x5c, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6e, 0x6f, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x5e, 0x5a, 0x5c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1050,63 +660,49 @@ func file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP() [] return file_teleport_notifications_v1_notifications_service_proto_rawDescData } -var file_teleport_notifications_v1_notifications_service_proto_msgTypes = make([]protoimpl.MessageInfo, 14) +var file_teleport_notifications_v1_notifications_service_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_teleport_notifications_v1_notifications_service_proto_goTypes = []interface{}{ (*CreateUserNotificationRequest)(nil), // 0: teleport.notifications.v1.CreateUserNotificationRequest (*DeleteUserNotificationRequest)(nil), // 1: teleport.notifications.v1.DeleteUserNotificationRequest - (*ListUserNotificationsRequest)(nil), // 2: teleport.notifications.v1.ListUserNotificationsRequest - (*ListUserNotificationsResponse)(nil), // 3: teleport.notifications.v1.ListUserNotificationsResponse + (*ListNotificationsRequest)(nil), // 2: teleport.notifications.v1.ListNotificationsRequest + (*ListNotificationsResponse)(nil), // 3: teleport.notifications.v1.ListNotificationsResponse (*CreateGlobalNotificationRequest)(nil), // 4: teleport.notifications.v1.CreateGlobalNotificationRequest (*DeleteGlobalNotificationRequest)(nil), // 5: teleport.notifications.v1.DeleteGlobalNotificationRequest (*UpsertUserNotificationStateRequest)(nil), // 6: teleport.notifications.v1.UpsertUserNotificationStateRequest - (*UpdateUserNotificationStateRequest)(nil), // 7: teleport.notifications.v1.UpdateUserNotificationStateRequest - (*DeleteUserNotificationStateRequest)(nil), // 8: teleport.notifications.v1.DeleteUserNotificationStateRequest - (*ListUserNotificationStatesRequest)(nil), // 9: teleport.notifications.v1.ListUserNotificationStatesRequest - (*ListUserNotificationStatesResponse)(nil), // 10: teleport.notifications.v1.ListUserNotificationStatesResponse - (*GetUserLastSeenNotificationRequest)(nil), // 11: teleport.notifications.v1.GetUserLastSeenNotificationRequest - (*UpsertUserLastSeenNotificationRequest)(nil), // 12: teleport.notifications.v1.UpsertUserLastSeenNotificationRequest - (*DeleteUserLastSeenNotificationRequest)(nil), // 13: teleport.notifications.v1.DeleteUserLastSeenNotificationRequest - (*Notification)(nil), // 14: teleport.notifications.v1.Notification - (*GlobalNotification)(nil), // 15: teleport.notifications.v1.GlobalNotification - (*UserNotificationState)(nil), // 16: teleport.notifications.v1.UserNotificationState - (*UserLastSeenNotification)(nil), // 17: teleport.notifications.v1.UserLastSeenNotification - (*emptypb.Empty)(nil), // 18: google.protobuf.Empty + (*UpsertUserLastSeenNotificationRequest)(nil), // 7: teleport.notifications.v1.UpsertUserLastSeenNotificationRequest + (*Notification)(nil), // 8: teleport.notifications.v1.Notification + (*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp + (*GlobalNotification)(nil), // 10: teleport.notifications.v1.GlobalNotification + (*UserNotificationState)(nil), // 11: teleport.notifications.v1.UserNotificationState + (*UserLastSeenNotification)(nil), // 12: teleport.notifications.v1.UserLastSeenNotification + (*emptypb.Empty)(nil), // 13: google.protobuf.Empty } var file_teleport_notifications_v1_notifications_service_proto_depIdxs = []int32{ - 14, // 0: teleport.notifications.v1.CreateUserNotificationRequest.notification:type_name -> teleport.notifications.v1.Notification - 14, // 1: teleport.notifications.v1.ListUserNotificationsResponse.notifications:type_name -> teleport.notifications.v1.Notification - 15, // 2: teleport.notifications.v1.CreateGlobalNotificationRequest.global_notification:type_name -> teleport.notifications.v1.GlobalNotification - 16, // 3: teleport.notifications.v1.UpsertUserNotificationStateRequest.user_notification_state:type_name -> teleport.notifications.v1.UserNotificationState - 16, // 4: teleport.notifications.v1.UpdateUserNotificationStateRequest.user_notification_state:type_name -> teleport.notifications.v1.UserNotificationState - 16, // 5: teleport.notifications.v1.ListUserNotificationStatesResponse.notification_states:type_name -> teleport.notifications.v1.UserNotificationState - 17, // 6: teleport.notifications.v1.UpsertUserLastSeenNotificationRequest.user_last_seen_notification:type_name -> teleport.notifications.v1.UserLastSeenNotification - 0, // 7: teleport.notifications.v1.NotificationService.CreateUserNotification:input_type -> teleport.notifications.v1.CreateUserNotificationRequest - 1, // 8: teleport.notifications.v1.NotificationService.DeleteUserNotification:input_type -> teleport.notifications.v1.DeleteUserNotificationRequest - 4, // 9: teleport.notifications.v1.NotificationService.CreateGlobalNotification:input_type -> teleport.notifications.v1.CreateGlobalNotificationRequest - 5, // 10: teleport.notifications.v1.NotificationService.DeleteGlobalNotification:input_type -> teleport.notifications.v1.DeleteGlobalNotificationRequest - 2, // 11: teleport.notifications.v1.NotificationService.ListUserNotifications:input_type -> teleport.notifications.v1.ListUserNotificationsRequest - 6, // 12: teleport.notifications.v1.NotificationService.UpsertUserNotificationState:input_type -> teleport.notifications.v1.UpsertUserNotificationStateRequest - 8, // 13: teleport.notifications.v1.NotificationService.DeleteUserNotificationState:input_type -> teleport.notifications.v1.DeleteUserNotificationStateRequest - 9, // 14: teleport.notifications.v1.NotificationService.ListUserNotificationStates:input_type -> teleport.notifications.v1.ListUserNotificationStatesRequest - 11, // 15: teleport.notifications.v1.NotificationService.GetUserLastSeenNotification:input_type -> teleport.notifications.v1.GetUserLastSeenNotificationRequest - 12, // 16: teleport.notifications.v1.NotificationService.UpsertUserLastSeenNotification:input_type -> teleport.notifications.v1.UpsertUserLastSeenNotificationRequest - 13, // 17: teleport.notifications.v1.NotificationService.DeleteUserLastSeenNotification:input_type -> teleport.notifications.v1.DeleteUserLastSeenNotificationRequest - 14, // 18: teleport.notifications.v1.NotificationService.CreateUserNotification:output_type -> teleport.notifications.v1.Notification - 18, // 19: teleport.notifications.v1.NotificationService.DeleteUserNotification:output_type -> google.protobuf.Empty - 15, // 20: teleport.notifications.v1.NotificationService.CreateGlobalNotification:output_type -> teleport.notifications.v1.GlobalNotification - 18, // 21: teleport.notifications.v1.NotificationService.DeleteGlobalNotification:output_type -> google.protobuf.Empty - 3, // 22: teleport.notifications.v1.NotificationService.ListUserNotifications:output_type -> teleport.notifications.v1.ListUserNotificationsResponse - 16, // 23: teleport.notifications.v1.NotificationService.UpsertUserNotificationState:output_type -> teleport.notifications.v1.UserNotificationState - 18, // 24: teleport.notifications.v1.NotificationService.DeleteUserNotificationState:output_type -> google.protobuf.Empty - 10, // 25: teleport.notifications.v1.NotificationService.ListUserNotificationStates:output_type -> teleport.notifications.v1.ListUserNotificationStatesResponse - 17, // 26: teleport.notifications.v1.NotificationService.GetUserLastSeenNotification:output_type -> teleport.notifications.v1.UserLastSeenNotification - 17, // 27: teleport.notifications.v1.NotificationService.UpsertUserLastSeenNotification:output_type -> teleport.notifications.v1.UserLastSeenNotification - 18, // 28: teleport.notifications.v1.NotificationService.DeleteUserLastSeenNotification:output_type -> google.protobuf.Empty - 18, // [18:29] is the sub-list for method output_type - 7, // [7:18] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 8, // 0: teleport.notifications.v1.CreateUserNotificationRequest.notification:type_name -> teleport.notifications.v1.Notification + 8, // 1: teleport.notifications.v1.ListNotificationsResponse.notifications:type_name -> teleport.notifications.v1.Notification + 9, // 2: teleport.notifications.v1.ListNotificationsResponse.user_last_seen_notification_timestamp:type_name -> google.protobuf.Timestamp + 10, // 3: teleport.notifications.v1.CreateGlobalNotificationRequest.global_notification:type_name -> teleport.notifications.v1.GlobalNotification + 11, // 4: teleport.notifications.v1.UpsertUserNotificationStateRequest.user_notification_state:type_name -> teleport.notifications.v1.UserNotificationState + 12, // 5: teleport.notifications.v1.UpsertUserLastSeenNotificationRequest.user_last_seen_notification:type_name -> teleport.notifications.v1.UserLastSeenNotification + 0, // 6: teleport.notifications.v1.NotificationService.CreateUserNotification:input_type -> teleport.notifications.v1.CreateUserNotificationRequest + 1, // 7: teleport.notifications.v1.NotificationService.DeleteUserNotification:input_type -> teleport.notifications.v1.DeleteUserNotificationRequest + 4, // 8: teleport.notifications.v1.NotificationService.CreateGlobalNotification:input_type -> teleport.notifications.v1.CreateGlobalNotificationRequest + 5, // 9: teleport.notifications.v1.NotificationService.DeleteGlobalNotification:input_type -> teleport.notifications.v1.DeleteGlobalNotificationRequest + 2, // 10: teleport.notifications.v1.NotificationService.ListNotifications:input_type -> teleport.notifications.v1.ListNotificationsRequest + 6, // 11: teleport.notifications.v1.NotificationService.UpsertUserNotificationState:input_type -> teleport.notifications.v1.UpsertUserNotificationStateRequest + 7, // 12: teleport.notifications.v1.NotificationService.UpsertUserLastSeenNotification:input_type -> teleport.notifications.v1.UpsertUserLastSeenNotificationRequest + 8, // 13: teleport.notifications.v1.NotificationService.CreateUserNotification:output_type -> teleport.notifications.v1.Notification + 13, // 14: teleport.notifications.v1.NotificationService.DeleteUserNotification:output_type -> google.protobuf.Empty + 10, // 15: teleport.notifications.v1.NotificationService.CreateGlobalNotification:output_type -> teleport.notifications.v1.GlobalNotification + 13, // 16: teleport.notifications.v1.NotificationService.DeleteGlobalNotification:output_type -> google.protobuf.Empty + 3, // 17: teleport.notifications.v1.NotificationService.ListNotifications:output_type -> teleport.notifications.v1.ListNotificationsResponse + 11, // 18: teleport.notifications.v1.NotificationService.UpsertUserNotificationState:output_type -> teleport.notifications.v1.UserNotificationState + 12, // 19: teleport.notifications.v1.NotificationService.UpsertUserLastSeenNotification:output_type -> teleport.notifications.v1.UserLastSeenNotification + 13, // [13:20] is the sub-list for method output_type + 6, // [6:13] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_teleport_notifications_v1_notifications_service_proto_init() } @@ -1141,7 +737,7 @@ func file_teleport_notifications_v1_notifications_service_proto_init() { } } file_teleport_notifications_v1_notifications_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListUserNotificationsRequest); i { + switch v := v.(*ListNotificationsRequest); i { case 0: return &v.state case 1: @@ -1153,7 +749,7 @@ func file_teleport_notifications_v1_notifications_service_proto_init() { } } file_teleport_notifications_v1_notifications_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListUserNotificationsResponse); i { + switch v := v.(*ListNotificationsResponse); i { case 0: return &v.state case 1: @@ -1201,66 +797,6 @@ func file_teleport_notifications_v1_notifications_service_proto_init() { } } file_teleport_notifications_v1_notifications_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateUserNotificationStateRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_teleport_notifications_v1_notifications_service_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteUserNotificationStateRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_teleport_notifications_v1_notifications_service_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListUserNotificationStatesRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_teleport_notifications_v1_notifications_service_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListUserNotificationStatesResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_teleport_notifications_v1_notifications_service_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetUserLastSeenNotificationRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_teleport_notifications_v1_notifications_service_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UpsertUserLastSeenNotificationRequest); i { case 0: return &v.state @@ -1272,18 +808,6 @@ func file_teleport_notifications_v1_notifications_service_proto_init() { return nil } } - file_teleport_notifications_v1_notifications_service_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteUserLastSeenNotificationRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } } type x struct{} out := protoimpl.TypeBuilder{ @@ -1291,7 +815,7 @@ func file_teleport_notifications_v1_notifications_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_teleport_notifications_v1_notifications_service_proto_rawDesc, NumEnums: 0, - NumMessages: 14, + NumMessages: 8, NumExtensions: 0, NumServices: 1, }, diff --git a/api/gen/proto/go/teleport/notifications/v1/notifications_service_grpc.pb.go b/api/gen/proto/go/teleport/notifications/v1/notifications_service_grpc.pb.go index 4c84d1a6a27..a00e1de288d 100644 --- a/api/gen/proto/go/teleport/notifications/v1/notifications_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/notifications/v1/notifications_service_grpc.pb.go @@ -41,13 +41,9 @@ const ( NotificationService_DeleteUserNotification_FullMethodName = "/teleport.notifications.v1.NotificationService/DeleteUserNotification" NotificationService_CreateGlobalNotification_FullMethodName = "/teleport.notifications.v1.NotificationService/CreateGlobalNotification" NotificationService_DeleteGlobalNotification_FullMethodName = "/teleport.notifications.v1.NotificationService/DeleteGlobalNotification" - NotificationService_ListUserNotifications_FullMethodName = "/teleport.notifications.v1.NotificationService/ListUserNotifications" + NotificationService_ListNotifications_FullMethodName = "/teleport.notifications.v1.NotificationService/ListNotifications" NotificationService_UpsertUserNotificationState_FullMethodName = "/teleport.notifications.v1.NotificationService/UpsertUserNotificationState" - NotificationService_DeleteUserNotificationState_FullMethodName = "/teleport.notifications.v1.NotificationService/DeleteUserNotificationState" - NotificationService_ListUserNotificationStates_FullMethodName = "/teleport.notifications.v1.NotificationService/ListUserNotificationStates" - NotificationService_GetUserLastSeenNotification_FullMethodName = "/teleport.notifications.v1.NotificationService/GetUserLastSeenNotification" NotificationService_UpsertUserLastSeenNotification_FullMethodName = "/teleport.notifications.v1.NotificationService/UpsertUserLastSeenNotification" - NotificationService_DeleteUserLastSeenNotification_FullMethodName = "/teleport.notifications.v1.NotificationService/DeleteUserLastSeenNotification" ) // NotificationServiceClient is the client API for NotificationService service. @@ -62,20 +58,12 @@ type NotificationServiceClient interface { CreateGlobalNotification(ctx context.Context, in *CreateGlobalNotificationRequest, opts ...grpc.CallOption) (*GlobalNotification, error) // DeleteGlobalNotification deletes a global notification. DeleteGlobalNotification(ctx context.Context, in *DeleteGlobalNotificationRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) - // ListUserNotificationsRequest is the request for listing a user's notifications, which include user-specific ones as well as global notifications that match them. - ListUserNotifications(ctx context.Context, in *ListUserNotificationsRequest, opts ...grpc.CallOption) (*ListUserNotificationsResponse, error) + // ListNotifications returns a paginated list of a user's notifications. + ListNotifications(ctx context.Context, in *ListNotificationsRequest, opts ...grpc.CallOption) (*ListNotificationsResponse, error) // UpsertUserNotificationState creates or updates a user notification state which records whether the user has clicked on or dismissed a notification. UpsertUserNotificationState(ctx context.Context, in *UpsertUserNotificationStateRequest, opts ...grpc.CallOption) (*UserNotificationState, error) - // DeleteUserNotificationState deletes a user notification state object. - DeleteUserNotificationState(ctx context.Context, in *DeleteUserNotificationStateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) - // ListUserNotificationStates returns a page of a user's notification states. - ListUserNotificationStates(ctx context.Context, in *ListUserNotificationStatesRequest, opts ...grpc.CallOption) (*ListUserNotificationStatesResponse, error) - // GetUserLastSeenNotification returns a user's last seen notification item. - GetUserLastSeenNotification(ctx context.Context, in *GetUserLastSeenNotificationRequest, opts ...grpc.CallOption) (*UserLastSeenNotification, error) // UpsertUserLastSeenNotification creates or updates a user's last seen notification item. UpsertUserLastSeenNotification(ctx context.Context, in *UpsertUserLastSeenNotificationRequest, opts ...grpc.CallOption) (*UserLastSeenNotification, error) - // DeleteUserLastSeenNotification deletes a user's last seen notification item. - DeleteUserLastSeenNotification(ctx context.Context, in *DeleteUserLastSeenNotificationRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) } type notificationServiceClient struct { @@ -122,9 +110,9 @@ func (c *notificationServiceClient) DeleteGlobalNotification(ctx context.Context return out, nil } -func (c *notificationServiceClient) ListUserNotifications(ctx context.Context, in *ListUserNotificationsRequest, opts ...grpc.CallOption) (*ListUserNotificationsResponse, error) { - out := new(ListUserNotificationsResponse) - err := c.cc.Invoke(ctx, NotificationService_ListUserNotifications_FullMethodName, in, out, opts...) +func (c *notificationServiceClient) ListNotifications(ctx context.Context, in *ListNotificationsRequest, opts ...grpc.CallOption) (*ListNotificationsResponse, error) { + out := new(ListNotificationsResponse) + err := c.cc.Invoke(ctx, NotificationService_ListNotifications_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -140,33 +128,6 @@ func (c *notificationServiceClient) UpsertUserNotificationState(ctx context.Cont return out, nil } -func (c *notificationServiceClient) DeleteUserNotificationState(ctx context.Context, in *DeleteUserNotificationStateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { - out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, NotificationService_DeleteUserNotificationState_FullMethodName, in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *notificationServiceClient) ListUserNotificationStates(ctx context.Context, in *ListUserNotificationStatesRequest, opts ...grpc.CallOption) (*ListUserNotificationStatesResponse, error) { - out := new(ListUserNotificationStatesResponse) - err := c.cc.Invoke(ctx, NotificationService_ListUserNotificationStates_FullMethodName, in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *notificationServiceClient) GetUserLastSeenNotification(ctx context.Context, in *GetUserLastSeenNotificationRequest, opts ...grpc.CallOption) (*UserLastSeenNotification, error) { - out := new(UserLastSeenNotification) - err := c.cc.Invoke(ctx, NotificationService_GetUserLastSeenNotification_FullMethodName, in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *notificationServiceClient) UpsertUserLastSeenNotification(ctx context.Context, in *UpsertUserLastSeenNotificationRequest, opts ...grpc.CallOption) (*UserLastSeenNotification, error) { out := new(UserLastSeenNotification) err := c.cc.Invoke(ctx, NotificationService_UpsertUserLastSeenNotification_FullMethodName, in, out, opts...) @@ -176,15 +137,6 @@ func (c *notificationServiceClient) UpsertUserLastSeenNotification(ctx context.C return out, nil } -func (c *notificationServiceClient) DeleteUserLastSeenNotification(ctx context.Context, in *DeleteUserLastSeenNotificationRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { - out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, NotificationService_DeleteUserLastSeenNotification_FullMethodName, in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - // NotificationServiceServer is the server API for NotificationService service. // All implementations must embed UnimplementedNotificationServiceServer // for forward compatibility @@ -197,20 +149,12 @@ type NotificationServiceServer interface { CreateGlobalNotification(context.Context, *CreateGlobalNotificationRequest) (*GlobalNotification, error) // DeleteGlobalNotification deletes a global notification. DeleteGlobalNotification(context.Context, *DeleteGlobalNotificationRequest) (*emptypb.Empty, error) - // ListUserNotificationsRequest is the request for listing a user's notifications, which include user-specific ones as well as global notifications that match them. - ListUserNotifications(context.Context, *ListUserNotificationsRequest) (*ListUserNotificationsResponse, error) + // ListNotifications returns a paginated list of a user's notifications. + ListNotifications(context.Context, *ListNotificationsRequest) (*ListNotificationsResponse, error) // UpsertUserNotificationState creates or updates a user notification state which records whether the user has clicked on or dismissed a notification. UpsertUserNotificationState(context.Context, *UpsertUserNotificationStateRequest) (*UserNotificationState, error) - // DeleteUserNotificationState deletes a user notification state object. - DeleteUserNotificationState(context.Context, *DeleteUserNotificationStateRequest) (*emptypb.Empty, error) - // ListUserNotificationStates returns a page of a user's notification states. - ListUserNotificationStates(context.Context, *ListUserNotificationStatesRequest) (*ListUserNotificationStatesResponse, error) - // GetUserLastSeenNotification returns a user's last seen notification item. - GetUserLastSeenNotification(context.Context, *GetUserLastSeenNotificationRequest) (*UserLastSeenNotification, error) // UpsertUserLastSeenNotification creates or updates a user's last seen notification item. UpsertUserLastSeenNotification(context.Context, *UpsertUserLastSeenNotificationRequest) (*UserLastSeenNotification, error) - // DeleteUserLastSeenNotification deletes a user's last seen notification item. - DeleteUserLastSeenNotification(context.Context, *DeleteUserLastSeenNotificationRequest) (*emptypb.Empty, error) mustEmbedUnimplementedNotificationServiceServer() } @@ -230,27 +174,15 @@ func (UnimplementedNotificationServiceServer) CreateGlobalNotification(context.C func (UnimplementedNotificationServiceServer) DeleteGlobalNotification(context.Context, *DeleteGlobalNotificationRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteGlobalNotification not implemented") } -func (UnimplementedNotificationServiceServer) ListUserNotifications(context.Context, *ListUserNotificationsRequest) (*ListUserNotificationsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListUserNotifications not implemented") +func (UnimplementedNotificationServiceServer) ListNotifications(context.Context, *ListNotificationsRequest) (*ListNotificationsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListNotifications not implemented") } func (UnimplementedNotificationServiceServer) UpsertUserNotificationState(context.Context, *UpsertUserNotificationStateRequest) (*UserNotificationState, error) { return nil, status.Errorf(codes.Unimplemented, "method UpsertUserNotificationState not implemented") } -func (UnimplementedNotificationServiceServer) DeleteUserNotificationState(context.Context, *DeleteUserNotificationStateRequest) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method DeleteUserNotificationState not implemented") -} -func (UnimplementedNotificationServiceServer) ListUserNotificationStates(context.Context, *ListUserNotificationStatesRequest) (*ListUserNotificationStatesResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListUserNotificationStates not implemented") -} -func (UnimplementedNotificationServiceServer) GetUserLastSeenNotification(context.Context, *GetUserLastSeenNotificationRequest) (*UserLastSeenNotification, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetUserLastSeenNotification not implemented") -} func (UnimplementedNotificationServiceServer) UpsertUserLastSeenNotification(context.Context, *UpsertUserLastSeenNotificationRequest) (*UserLastSeenNotification, error) { return nil, status.Errorf(codes.Unimplemented, "method UpsertUserLastSeenNotification not implemented") } -func (UnimplementedNotificationServiceServer) DeleteUserLastSeenNotification(context.Context, *DeleteUserLastSeenNotificationRequest) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method DeleteUserLastSeenNotification not implemented") -} func (UnimplementedNotificationServiceServer) mustEmbedUnimplementedNotificationServiceServer() {} // UnsafeNotificationServiceServer may be embedded to opt out of forward compatibility for this service. @@ -336,20 +268,20 @@ func _NotificationService_DeleteGlobalNotification_Handler(srv interface{}, ctx return interceptor(ctx, in, info, handler) } -func _NotificationService_ListUserNotifications_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListUserNotificationsRequest) +func _NotificationService_ListNotifications_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListNotificationsRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(NotificationServiceServer).ListUserNotifications(ctx, in) + return srv.(NotificationServiceServer).ListNotifications(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: NotificationService_ListUserNotifications_FullMethodName, + FullMethod: NotificationService_ListNotifications_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(NotificationServiceServer).ListUserNotifications(ctx, req.(*ListUserNotificationsRequest)) + return srv.(NotificationServiceServer).ListNotifications(ctx, req.(*ListNotificationsRequest)) } return interceptor(ctx, in, info, handler) } @@ -372,60 +304,6 @@ func _NotificationService_UpsertUserNotificationState_Handler(srv interface{}, c return interceptor(ctx, in, info, handler) } -func _NotificationService_DeleteUserNotificationState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteUserNotificationStateRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(NotificationServiceServer).DeleteUserNotificationState(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: NotificationService_DeleteUserNotificationState_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(NotificationServiceServer).DeleteUserNotificationState(ctx, req.(*DeleteUserNotificationStateRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _NotificationService_ListUserNotificationStates_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListUserNotificationStatesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(NotificationServiceServer).ListUserNotificationStates(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: NotificationService_ListUserNotificationStates_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(NotificationServiceServer).ListUserNotificationStates(ctx, req.(*ListUserNotificationStatesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _NotificationService_GetUserLastSeenNotification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetUserLastSeenNotificationRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(NotificationServiceServer).GetUserLastSeenNotification(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: NotificationService_GetUserLastSeenNotification_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(NotificationServiceServer).GetUserLastSeenNotification(ctx, req.(*GetUserLastSeenNotificationRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _NotificationService_UpsertUserLastSeenNotification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(UpsertUserLastSeenNotificationRequest) if err := dec(in); err != nil { @@ -444,24 +322,6 @@ func _NotificationService_UpsertUserLastSeenNotification_Handler(srv interface{} return interceptor(ctx, in, info, handler) } -func _NotificationService_DeleteUserLastSeenNotification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteUserLastSeenNotificationRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(NotificationServiceServer).DeleteUserLastSeenNotification(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: NotificationService_DeleteUserLastSeenNotification_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(NotificationServiceServer).DeleteUserLastSeenNotification(ctx, req.(*DeleteUserLastSeenNotificationRequest)) - } - return interceptor(ctx, in, info, handler) -} - // NotificationService_ServiceDesc is the grpc.ServiceDesc for NotificationService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -486,33 +346,17 @@ var NotificationService_ServiceDesc = grpc.ServiceDesc{ Handler: _NotificationService_DeleteGlobalNotification_Handler, }, { - MethodName: "ListUserNotifications", - Handler: _NotificationService_ListUserNotifications_Handler, + MethodName: "ListNotifications", + Handler: _NotificationService_ListNotifications_Handler, }, { MethodName: "UpsertUserNotificationState", Handler: _NotificationService_UpsertUserNotificationState_Handler, }, - { - MethodName: "DeleteUserNotificationState", - Handler: _NotificationService_DeleteUserNotificationState_Handler, - }, - { - MethodName: "ListUserNotificationStates", - Handler: _NotificationService_ListUserNotificationStates_Handler, - }, - { - MethodName: "GetUserLastSeenNotification", - Handler: _NotificationService_GetUserLastSeenNotification_Handler, - }, { MethodName: "UpsertUserLastSeenNotification", Handler: _NotificationService_UpsertUserLastSeenNotification_Handler, }, - { - MethodName: "DeleteUserLastSeenNotification", - Handler: _NotificationService_DeleteUserLastSeenNotification_Handler, - }, }, Streams: []grpc.StreamDesc{}, Metadata: "teleport/notifications/v1/notifications_service.proto", diff --git a/api/proto/teleport/notifications/v1/notifications.proto b/api/proto/teleport/notifications/v1/notifications.proto index 13b4c82e7d8..65bb366b34f 100644 --- a/api/proto/teleport/notifications/v1/notifications.proto +++ b/api/proto/teleport/notifications/v1/notifications.proto @@ -42,8 +42,8 @@ message Notification { // NotificationSpec is the notification specification. message NotificationSpec { - // id is the UUID of the notification, in UUIDv7 format. - string id = 1; + reserved 1; + reserved "id"; // created is when the notification was created, in UNIX time. google.protobuf.Timestamp created = 2; // unscoped is whether the notification shouldn't be restricted to a specific audience. This is to prevent the potential future possibility that a user-specific notification contains information that the user should no longer be allowed to see. Default is true. diff --git a/api/proto/teleport/notifications/v1/notifications_service.proto b/api/proto/teleport/notifications/v1/notifications_service.proto index 3d9ce708d83..ce3f53579c3 100644 --- a/api/proto/teleport/notifications/v1/notifications_service.proto +++ b/api/proto/teleport/notifications/v1/notifications_service.proto @@ -21,6 +21,7 @@ syntax = "proto3"; package teleport.notifications.v1; import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; import "teleport/notifications/v1/notifications.proto"; option go_package = "github.com/gravitational/teleport/api/gen/proto/go/teleport/notifications/v1;notificationsv1"; @@ -37,22 +38,14 @@ service NotificationService { // DeleteGlobalNotification deletes a global notification. rpc DeleteGlobalNotification(DeleteGlobalNotificationRequest) returns (google.protobuf.Empty); - // ListUserNotificationsRequest is the request for listing a user's notifications, which include user-specific ones as well as global notifications that match them. - rpc ListUserNotifications(ListUserNotificationsRequest) returns (ListUserNotificationsResponse); + // ListNotifications returns a paginated list of a user's notifications. + rpc ListNotifications(ListNotificationsRequest) returns (ListNotificationsResponse); // UpsertUserNotificationState creates or updates a user notification state which records whether the user has clicked on or dismissed a notification. rpc UpsertUserNotificationState(UpsertUserNotificationStateRequest) returns (UserNotificationState); - // DeleteUserNotificationState deletes a user notification state object. - rpc DeleteUserNotificationState(DeleteUserNotificationStateRequest) returns (google.protobuf.Empty); - // ListUserNotificationStates returns a page of a user's notification states. - rpc ListUserNotificationStates(ListUserNotificationStatesRequest) returns (ListUserNotificationStatesResponse); - // GetUserLastSeenNotification returns a user's last seen notification item. - rpc GetUserLastSeenNotification(GetUserLastSeenNotificationRequest) returns (UserLastSeenNotification); // UpsertUserLastSeenNotification creates or updates a user's last seen notification item. rpc UpsertUserLastSeenNotification(UpsertUserLastSeenNotificationRequest) returns (UserLastSeenNotification); - // DeleteUserLastSeenNotification deletes a user's last seen notification item. - rpc DeleteUserLastSeenNotification(DeleteUserLastSeenNotificationRequest) returns (google.protobuf.Empty); } // CreateUserNotificationRequest is the request for creating a user-specific notification. @@ -71,22 +64,22 @@ message DeleteUserNotificationRequest { string notification_id = 2; } -// ListUserNotificationsRequest is the request for listing a user's notifications, which include user-specific ones as well as global notifications that match them. -message ListUserNotificationsRequest { - // username is the username of the user the notifications to list are for. - string username = 1; +// ListNotificationsRequest is the request for listing a user's notifications. +message ListNotificationsRequest { // page_size is the size of the page to return. - int32 page_size = 2; + int32 page_size = 1; // page_token is the next_page_token value returned from a previous ListUserNotifications request, if any. - string page_token = 3; + string page_token = 2; } -// ListUserNotificationsResponse is the response from listing a user's user-specific notifications. -message ListUserNotificationsResponse { +// ListNotificationsResponse is the response from listing a user's notifications. +message ListNotificationsResponse { // notifications is the notifications returned. repeated Notification notifications = 1; - // next_page_token is the token to retrieve the next page of results, this will be empty if there are no more results. - string next_page_token = 2; + // next_page_token contains the next page token to use as the start key for the next page of notifications. + string next_page_token = 3; + // user_last_seen_notification_timestamp is the timestamp of the last notification the user has seen. + google.protobuf.Timestamp user_last_seen_notification_timestamp = 4; } // CreateGlobalNotificationRequest is the request for creating a global notification. @@ -109,52 +102,10 @@ message UpsertUserNotificationStateRequest { UserNotificationState user_notification_state = 2; } -// UpdateUserNotificationStateRequest is the request for updating a user notification state. -message UpdateUserNotificationStateRequest { - // username is the username of the user. - string username = 1; - // user_notification_state is the updated user notification state. - UserNotificationState user_notification_state = 2; -} - -// DeleteUserNotificationState is the request for deleting a user notification state. -message DeleteUserNotificationStateRequest { - // username is the username of the user. - string username = 1; - // notification_id is the ID of the notification the notifiation state to delete is for. - string notification_id = 2; -} - -// ListUserNotificationStatesRequest is the request for listing a user's notification states. -message ListUserNotificationStatesRequest { - // username is the username of the user. - string username = 1; -} - -// ListUserNotificationStatesResponse is the response from listing a user's notification states. -message ListUserNotificationStatesResponse { - // notification_states is the user notification state items returned. - repeated UserNotificationState notification_states = 1; - // next_page_token is the token to retrieve the next page of results, this will be empty if there are no more results. - string next_page_token = 2; -} - -// GetUserLastSeenNotificationRequest is the request for getting a user's last seen notification. -message GetUserLastSeenNotificationRequest { - // username is the username of the user. - string username = 1; -} - // UpsertUserLastSeenNotificationRequest is the request for creating or updating a user's last seen notification. message UpsertUserLastSeenNotificationRequest { // username is the username of the user. string username = 1; - // user_notification_state is the udpated user last seen notification item. + // user_notification_state is the updated user last seen notification item. UserLastSeenNotification user_last_seen_notification = 2; } - -// DeleteUserLastSeenNotificationRequest is the request for deleting a user's last seen notification. -message DeleteUserLastSeenNotificationRequest { - // username is the username of the user. - string username = 1; -} diff --git a/api/types/constants.go b/api/types/constants.go index 2ce0c522252..ac4912ef805 100644 --- a/api/types/constants.go +++ b/api/types/constants.go @@ -986,6 +986,15 @@ const ( PluginGenerationLabel = TeleportInternalLabelPrefix + "plugin-generation" ) +const ( + // NotificationTitleLabel is the label which contains the title of the notification. + NotificationTitleLabel = TeleportInternalLabelPrefix + "title" + // NotificationClickedLabel is the label which contains whether the notification has been clicked on by the user. + NotificationClickedLabel = TeleportInternalLabelPrefix + "clicked" + // NotificationScope is the label which contains the scope of the notification, either "user" or "global" + NotificationScope = TeleportInternalLabelPrefix + "scope" +) + const ( // InstallMethodAWSOIDCDeployServiceEnvVar is the env var used to detect if the agent was installed // using the DeployService action of the AWS OIDC integration. diff --git a/lib/auth/authclient/clt.go b/lib/auth/authclient/clt.go index 43faf8ef6b6..e9840a71046 100644 --- a/lib/auth/authclient/clt.go +++ b/lib/auth/authclient/clt.go @@ -629,6 +629,10 @@ func (c *Client) IntegrationAWSOIDCClient() integrationv1.AWSOIDCServiceClient { return integrationv1.NewAWSOIDCServiceClient(c.APIClient.GetConnection()) } +func (c *Client) NotificationServiceClient() notificationsv1.NotificationServiceClient { + return notificationsv1.NewNotificationServiceClient(c.APIClient.GetConnection()) +} + // DiscoveryConfigClient returns a client for managing the DiscoveryConfig resource. func (c *Client) DiscoveryConfigClient() services.DiscoveryConfigWithStatusUpdater { return c.APIClient.DiscoveryConfigClient() @@ -654,6 +658,22 @@ func (c *Client) SetStaticTokens(st types.StaticTokens) error { return trace.NotImplemented(notImplementedMessage) } +// UpsertUserNotificationState creates or updates a user notification state which records whether the user has clicked on or dismissed a notification. +func (c *Client) UpsertUserNotificationState(ctx context.Context, username string, uns *notificationsv1.UserNotificationState) (*notificationsv1.UserNotificationState, error) { + return c.APIClient.UpsertUserNotificationState(ctx, ¬ificationsv1.UpsertUserNotificationStateRequest{ + Username: username, + UserNotificationState: uns, + }) +} + +// UpsertUserLastSeenNotification creates or updates a user's last seen notification item. +func (c *Client) UpsertUserLastSeenNotification(ctx context.Context, username string, ulsn *notificationsv1.UserLastSeenNotification) (*notificationsv1.UserLastSeenNotification, error) { + return c.APIClient.UpsertUserLastSeenNotification(ctx, ¬ificationsv1.UpsertUserLastSeenNotificationRequest{ + Username: username, + UserLastSeenNotification: ulsn, + }) +} + // CreateGlobalNotification creates a global notification. func (c *Client) CreateGlobalNotification(ctx context.Context, globalNotification *notificationsv1.GlobalNotification) (*notificationsv1.GlobalNotification, error) { // TODO(rudream): implement client methods for notifications @@ -672,18 +692,6 @@ func (c *Client) DeleteGlobalNotification(ctx context.Context, notificationId st return trace.NotImplemented(notImplementedMessage) } -// UpsertUserNotificationState creates or updates a user notification state which records whether the user has clicked on or dismissed a notification. -func (c *Client) UpsertUserNotificationState(ctx context.Context, username string, state *notificationsv1.UserNotificationState) (*notificationsv1.UserNotificationState, error) { - // TODO(rudream): implement client methods for notifications - return nil, trace.NotImplemented(notImplementedMessage) -} - -// UpsertUserLastSeenNotification creates or updates a user's last seen notification item. -func (c *Client) UpsertUserLastSeenNotification(ctx context.Context, username string, ulsn *notificationsv1.UserLastSeenNotification) (*notificationsv1.UserLastSeenNotification, error) { - // TODO(rudream): implement client methods for notifications - return nil, trace.NotImplemented(notImplementedMessage) -} - // DeleteAllGlobalNotifications not implemented: can only be called locally. func (c *Client) DeleteAllGlobalNotifications(ctx context.Context) error { return trace.NotImplemented(notImplementedMessage) @@ -1608,6 +1616,12 @@ type ClientI interface { // (as per the default gRPC behavior). WorkloadIdentityServiceClient() machineidv1pb.WorkloadIdentityServiceClient + // NotificationServiceClient returns a notification service client. + // Clients connecting to older Teleport versions, still get a client + // when calling this method, but all RPCs will return "not implemented" errors + // (as per the default gRPC behavior). + NotificationServiceClient() notificationsv1.NotificationServiceClient + // ClusterConfigClient returns a Cluster Configuration client. // Clients connecting to non-Enterprise clusters, or older Teleport versions, // still get a client when calling this method, but all RPCs will return diff --git a/lib/auth/grpcserver.go b/lib/auth/grpcserver.go index 103800b7175..8cff469d22b 100644 --- a/lib/auth/grpcserver.go +++ b/lib/auth/grpcserver.go @@ -60,6 +60,7 @@ import ( loginrulepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/loginrule/v1" machineidv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/machineid/v1" mfav1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/mfa/v1" + notificationsv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/notifications/v1" oktapb "github.com/gravitational/teleport/api/gen/proto/go/teleport/okta/v1" presencev1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/presence/v1" trustpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/trust/v1" @@ -83,6 +84,7 @@ import ( kubewaitingcontainerv1 "github.com/gravitational/teleport/lib/auth/kubewaitingcontainer" "github.com/gravitational/teleport/lib/auth/loginrule" "github.com/gravitational/teleport/lib/auth/machineid/machineidv1" + notifications "github.com/gravitational/teleport/lib/auth/notifications/notificationsv1" "github.com/gravitational/teleport/lib/auth/okta" "github.com/gravitational/teleport/lib/auth/presence/presencev1" "github.com/gravitational/teleport/lib/auth/trust/trustv1" @@ -5300,6 +5302,18 @@ func NewGRPCServer(cfg GRPCServerConfig) (*GRPCServer, error) { } accessmonitoringrules.RegisterAccessMonitoringRulesServiceServer(server, accessMonitoringRuleServer) + // Initialize and register the notifications service. + notificationsServer, err := notifications.NewService(notifications.ServiceConfig{ + Authorizer: cfg.Authorizer, + Backend: cfg.AuthServer.Services, + UserNotificationCache: cfg.AuthServer.UserNotificationCache, + GlobalNotificationCache: cfg.AuthServer.GlobalNotificationCache, + }) + if err != nil { + return nil, trace.Wrap(err) + } + notificationsv1.RegisterNotificationServiceServer(server, notificationsServer) + // Only register the service if this is an open source build. Enterprise builds // register the actual service via an auth plugin, if we register here then all // Enterprise builds would fail with a duplicate service registered error. diff --git a/lib/auth/helpers.go b/lib/auth/helpers.go index 712c8248c52..c262cb03264 100644 --- a/lib/auth/helpers.go +++ b/lib/auth/helpers.go @@ -298,6 +298,7 @@ func NewTestAuthServer(cfg TestAuthServerConfig) (*TestAuthServer, error) { SkipPeriodicOperations: true, Emitter: emitter, TraceClient: cfg.TraceClient, + Clock: cfg.Clock, KeyStoreConfig: keystore.Config{ Software: keystore.SoftwareConfig{ RSAKeyPairSource: authority.New().GenerateKeyPair, diff --git a/lib/auth/notification_test.go b/lib/auth/notification_test.go new file mode 100644 index 00000000000..3a2feec7e8b --- /dev/null +++ b/lib/auth/notification_test.go @@ -0,0 +1,445 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package auth + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/gravitational/trace" + "github.com/jonboulle/clockwork" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/timestamppb" + + headerv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + notificationsv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/notifications/v1" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/services" +) + +func TestNotifications(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + fakeClock := clockwork.NewFakeClock() + + authServer, err := NewTestAuthServer(TestAuthServerConfig{ + Dir: t.TempDir(), + Clock: fakeClock, + CacheEnabled: true, + }) + require.NoError(t, err) + + srv, err := authServer.NewTestTLSServer() + + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, srv.Close()) }) + + roles := map[string]types.RoleSpecV6{ + // auditors have access to nodes and databases, and the "auditor" and "user" logins + "auditors": { + Allow: types.RoleConditions{ + Logins: []string{"auditor", "user"}, + Rules: []types.Rule{ + { + Resources: []string{types.KindNode, types.KindDatabase}, + Verbs: services.RW(), + }, + }, + }, + }, + // managers have access to review requests for the "intern" role + "managers": { + Allow: types.RoleConditions{ + Logins: []string{"user"}, + ReviewRequests: &types.AccessReviewConditions{ + Roles: []string{"intern"}, + }, + }, + }, + } + for roleName, roleSpec := range roles { + role, err := types.NewRole(roleName, roleSpec) + require.NoError(t, err) + + _, err = srv.Auth().UpsertRole(ctx, role) + require.NoError(t, err) + } + + auditorUsername := "auditor" + managerUsername := "manager" + + userRolesMap := map[string][]string{ + auditorUsername: {"auditors"}, + managerUsername: {"managers"}, + } + + // Create the users with their roles. + for username, roles := range userRolesMap { + user, err := types.NewUser(username) + require.NoError(t, err) + user.SetRoles(roles) + _, err = srv.Auth().UpsertUser(ctx, user) + require.NoError(t, err) + } + + // Describes a set of mock notifications to be created. + // The number in each notification's description represents the creation order of the notifications. + // eg. "auditor-3" will be the third notification created for auditor, and the third from last in the list (since it's the third oldest), + testNotifications := []struct { + userNotification *notificationsv1.Notification + globalNotification *notificationsv1.GlobalNotification + }{ + { + userNotification: newUserNotification(t, auditorUsername, "auditor-1"), + }, + { + userNotification: newUserNotification(t, auditorUsername, "auditor-2"), + }, + { + // Matcher matches by the role "auditors" + globalNotification: newGlobalNotification(t, "auditor-3", ¬ificationsv1.GlobalNotificationSpec{ + Matcher: ¬ificationsv1.GlobalNotificationSpec_ByRoles{ + ByRoles: ¬ificationsv1.ByRoles{ + Roles: []string{userRolesMap[auditorUsername][0]}, + }, + }, + }), + }, + { + // Matcher matches by the role "managers" + globalNotification: newGlobalNotification(t, "manager-1", ¬ificationsv1.GlobalNotificationSpec{ + Matcher: ¬ificationsv1.GlobalNotificationSpec_ByRoles{ + ByRoles: ¬ificationsv1.ByRoles{ + Roles: []string{userRolesMap[managerUsername][0]}, + }, + }, + }), + }, + { + userNotification: newUserNotification(t, auditorUsername, "auditor-4"), + }, + { + // Matcher matches all, both users should see this. + globalNotification: newGlobalNotification(t, "auditor-5,manager-2", ¬ificationsv1.GlobalNotificationSpec{ + Matcher: ¬ificationsv1.GlobalNotificationSpec_All{ + All: true, + }, + }), + }, + { + userNotification: newUserNotification(t, managerUsername, "manager-3"), + }, + { + // Matcher matches by read & write permission on nodes. + globalNotification: newGlobalNotification(t, "auditor-6", ¬ificationsv1.GlobalNotificationSpec{ + Matcher: ¬ificationsv1.GlobalNotificationSpec_ByPermissions{ + ByPermissions: ¬ificationsv1.ByPermissions{ + RoleConditions: []*types.RoleConditions{ + { + Rules: []types.Rule{ + { + Resources: []string{types.KindNode}, + Verbs: services.RW(), + }, + }, + }, + }, + }, + }, + }), + }, + { + // Matcher matches by the logins "auditor" and "user". Auditor has both of them, but manager only has "user", since we set MatchAllConditions to true, only auditor + // should get this notification. + globalNotification: newGlobalNotification(t, "auditor-7", ¬ificationsv1.GlobalNotificationSpec{ + Matcher: ¬ificationsv1.GlobalNotificationSpec_ByPermissions{ + ByPermissions: ¬ificationsv1.ByPermissions{ + RoleConditions: []*types.RoleConditions{ + { + Logins: []string{"auditor"}, + }, + { + Logins: []string{"user"}, + }, + }, + }, + }, + MatchAllConditions: true, + }), + }, + { + // Matcher matches by permission to review access requests for "intern" + globalNotification: newGlobalNotification(t, "manager-4", ¬ificationsv1.GlobalNotificationSpec{ + Matcher: ¬ificationsv1.GlobalNotificationSpec_ByPermissions{ + ByPermissions: ¬ificationsv1.ByPermissions{ + RoleConditions: []*types.RoleConditions{ + { + ReviewRequests: &types.AccessReviewConditions{ + Roles: []string{"intern"}, + }, + }, + }, + }, + }, + }), + }, + { + userNotification: newUserNotification(t, managerUsername, "manager-5"), + }, + { + // Matcher matches by the logins "auditor" and "user". Both of them have "user" and MatchAllConditions is false, so both should get this notification. + globalNotification: newGlobalNotification(t, "auditor-8,manager-6", ¬ificationsv1.GlobalNotificationSpec{ + Matcher: ¬ificationsv1.GlobalNotificationSpec_ByPermissions{ + ByPermissions: ¬ificationsv1.ByPermissions{ + RoleConditions: []*types.RoleConditions{ + { + Logins: []string{"auditor"}, + }, + { + Logins: []string{"user"}, + }, + }, + }, + }, + }), + }, + } + + notificationIdMap := map[string]string{} + + // Create the notifications. + for _, n := range testNotifications { + // We add a small delay to ensure that the timestamps in the generated UUID's are all different and in the correct order. + // This is to prevent flakiness caused by the notifications being created in such quick succession that the timestamps are the same. + fakeClock.Advance(50 * time.Millisecond) + time.Sleep(50 * time.Millisecond) + // Create the notification in the backend. + if n.globalNotification != nil { + created, err := srv.Auth().CreateGlobalNotification(ctx, n.globalNotification) + notificationIdMap[created.GetSpec().GetNotification().GetMetadata().GetLabels()[types.NotificationTitleLabel]] = created.GetMetadata().GetName() + require.NoError(t, err) + continue + } + created, err := srv.Auth().CreateUserNotification(ctx, n.userNotification) + notificationIdMap[created.GetMetadata().GetLabels()[types.NotificationTitleLabel]] = created.GetMetadata().GetName() + require.NoError(t, err) + } + + // Test fetching notifications for auditor. + auditorClient, err := srv.NewClient(TestUser(auditorUsername)) + require.NoError(t, err) + defer auditorClient.Close() + + auditorExpectedNotifications := []string{"auditor-8,manager-6", "auditor-7", "auditor-6", "auditor-5,manager-2", "auditor-4", "auditor-3", "auditor-2", "auditor-1"} + + var finalOut []*notificationsv1.Notification + + // Upsert auditor's last seen timestamp. + lastSeenTimestamp := timestamppb.New(time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC)) + _, err = auditorClient.UpsertUserLastSeenNotification(ctx, auditorUsername, ¬ificationsv1.UserLastSeenNotification{ + Status: ¬ificationsv1.UserLastSeenNotificationStatus{ + LastSeenTime: lastSeenTimestamp, + }, + }) + require.NoError(t, err) + + // Fetch a page of 3 notifications. + resp, err := auditorClient.ListNotifications(ctx, ¬ificationsv1.ListNotificationsRequest{ + PageSize: 3, + }) + require.NoError(t, err) + require.Equal(t, auditorExpectedNotifications[:3], notificationsToTitlesList(t, resp.Notifications)) + finalOut = append(finalOut, resp.Notifications...) + + // Verify that the nextKeys are correct. + expectedUserNotifsNextKey := notificationIdMap["auditor-4"] + expectedGlobalNotifsNextKey := notificationIdMap["auditor-5,manager-2"] + expectedNextKeys := fmt.Sprintf("%s,%s", + expectedUserNotifsNextKey, + expectedGlobalNotifsNextKey) // "," + + require.Equal(t, expectedNextKeys, resp.NextPageToken) + require.Equal(t, lastSeenTimestamp.GetSeconds(), resp.UserLastSeenNotificationTimestamp.GetSeconds()) + + // Fetch the next 3 notifications, starting from the previously received startKeys. + // After this fetch, there should be no more global notifications for auditor, so the next page token + // for global notifications should be "end". + resp, err = auditorClient.ListNotifications(ctx, ¬ificationsv1.ListNotificationsRequest{ + PageSize: 3, + PageToken: resp.NextPageToken, + }) + require.NoError(t, err) + expectedNextKeys = fmt.Sprintf("%s,", notificationIdMap["auditor-2"]) + + require.Equal(t, expectedNextKeys, resp.NextPageToken) + finalOut = append(finalOut, resp.Notifications...) + + // Fetch the rest of the notifications, starting from the previously received startKeys. + resp, err = auditorClient.ListNotifications(ctx, ¬ificationsv1.ListNotificationsRequest{ + PageSize: 10, + PageToken: resp.NextPageToken, + }) + require.NoError(t, err) + finalOut = append(finalOut, resp.Notifications...) + // Verify that all the notifications are in the list and in correct order. + require.Equal(t, auditorExpectedNotifications, notificationsToTitlesList(t, finalOut)) + // Verify that we've reached the end of both lists. + require.Equal(t, "", resp.NextPageToken) + + // Mark "auditor-2" and "auditor-5,manager-2" as dismissed. + _, err = auditorClient.UpsertUserNotificationState(ctx, auditorUsername, ¬ificationsv1.UserNotificationState{ + Spec: ¬ificationsv1.UserNotificationStateSpec{ + NotificationId: notificationIdMap["auditor-2"], + }, + Status: ¬ificationsv1.UserNotificationStateStatus{ + NotificationState: notificationsv1.NotificationState_NOTIFICATION_STATE_DISMISSED, + }, + }) + require.NoError(t, err) + _, err = auditorClient.UpsertUserNotificationState(ctx, auditorUsername, ¬ificationsv1.UserNotificationState{ + Spec: ¬ificationsv1.UserNotificationStateSpec{ + NotificationId: notificationIdMap["auditor-5,manager-2"], + }, + Status: ¬ificationsv1.UserNotificationStateStatus{ + NotificationState: notificationsv1.NotificationState_NOTIFICATION_STATE_DISMISSED, + }, + }) + require.NoError(t, err) + + // Fetch notifications again and verify that the dismissed notifications are not returned. + resp, err = auditorClient.ListNotifications(ctx, ¬ificationsv1.ListNotificationsRequest{ + PageSize: 10, + }) + auditorExpectedNotifsAfterDismissal := []string{"auditor-8,manager-6", "auditor-7", "auditor-6", "auditor-4", "auditor-3", "auditor-1"} + require.NoError(t, err) + require.Equal(t, auditorExpectedNotifsAfterDismissal, notificationsToTitlesList(t, resp.Notifications)) + + // Test fetching notifications for manager. + managerClient, err := srv.NewClient(TestUser(managerUsername)) + require.NoError(t, err) + defer managerClient.Close() + + managerExpectedNotifications := []string{"auditor-8,manager-6", "manager-5", "manager-4", "manager-3", "auditor-5,manager-2", "manager-1"} + + resp, err = managerClient.ListNotifications(ctx, ¬ificationsv1.ListNotificationsRequest{ + PageSize: 10, + }) + require.NoError(t, err) + + require.Equal(t, managerExpectedNotifications, notificationsToTitlesList(t, resp.Notifications)) + // Verify that we've reached the end of both lists. + require.Equal(t, "", resp.NextPageToken) + + // Mark "auditor-8,manager-6" as clicked. + _, err = managerClient.UpsertUserNotificationState(ctx, managerUsername, ¬ificationsv1.UserNotificationState{ + Spec: ¬ificationsv1.UserNotificationStateSpec{ + NotificationId: notificationIdMap["auditor-8,manager-6"], + }, + Status: ¬ificationsv1.UserNotificationStateStatus{ + NotificationState: notificationsv1.NotificationState_NOTIFICATION_STATE_CLICKED, + }, + }) + require.NoError(t, err) + + // Fetch notifications again and expect it to have the clicked label. + resp, err = managerClient.ListNotifications(ctx, ¬ificationsv1.ListNotificationsRequest{ + PageSize: 10, + }) + require.NoError(t, err) + + clickedNotification := resp.Notifications[0] // "auditor-8,manager-6" is the first item in the list + clickedLabelValue := clickedNotification.GetMetadata().GetLabels()[types.NotificationClickedLabel] + require.Equal(t, "true", clickedLabelValue) + + // Verify that manager can't upsert a notification state for auditor + _, err = managerClient.UpsertUserNotificationState(ctx, auditorUsername, ¬ificationsv1.UserNotificationState{ + Spec: ¬ificationsv1.UserNotificationStateSpec{ + NotificationId: notificationIdMap["auditor-7"], + }, + Status: ¬ificationsv1.UserNotificationStateStatus{ + NotificationState: notificationsv1.NotificationState_NOTIFICATION_STATE_DISMISSED, + }, + }) + require.Error(t, err) + require.True(t, trace.IsAccessDenied(err), "got error %T, expected an access denied error due to manager trying to upsert a notification state for a different user", err) + + // Verify that manager can't uspert a user last seen notification for auditor. + _, err = managerClient.UpsertUserLastSeenNotification(ctx, auditorUsername, ¬ificationsv1.UserLastSeenNotification{ + Status: ¬ificationsv1.UserLastSeenNotificationStatus{ + LastSeenTime: lastSeenTimestamp, + }, + }) + require.Error(t, err) + require.True(t, trace.IsAccessDenied(err), "got error %T, expected an access denied error due to manager trying to upsert a last seen notification timestamp for a different user", err) +} + +func newUserNotification(t *testing.T, username string, title string) *notificationsv1.Notification { + t.Helper() + + notification := notificationsv1.Notification{ + SubKind: "test-subkind", + Spec: ¬ificationsv1.NotificationSpec{ + Username: username, + }, + Metadata: &headerv1.Metadata{ + Labels: map[string]string{ + types.NotificationTitleLabel: title, + }, + }, + } + + return ¬ification +} + +func newGlobalNotification(t *testing.T, title string, spec *notificationsv1.GlobalNotificationSpec) *notificationsv1.GlobalNotification { + t.Helper() + + spec.Notification = ¬ificationsv1.Notification{ + SubKind: "test-subkind", + Spec: ¬ificationsv1.NotificationSpec{}, + Metadata: &headerv1.Metadata{ + Labels: map[string]string{ + types.NotificationTitleLabel: title, + }, + }, + } + + notification := notificationsv1.GlobalNotification{ + Spec: spec, + } + + return ¬ification +} + +// notificationsToTitlesList accepts a list of notifications notifications and returns a slice of strings containing their titles in order, this is used to compare against +// the expected outputs. +func notificationsToTitlesList(t *testing.T, notifications []*notificationsv1.Notification) []string { + t.Helper() + var descriptions []string + + for _, notif := range notifications { + description := notif.GetMetadata().GetLabels()[types.NotificationTitleLabel] + descriptions = append(descriptions, description) + } + + return descriptions +} diff --git a/lib/auth/notifications/notificationsv1/service.go b/lib/auth/notifications/notificationsv1/service.go new file mode 100644 index 00000000000..3abf265c22b --- /dev/null +++ b/lib/auth/notifications/notificationsv1/service.go @@ -0,0 +1,446 @@ +// Copyright 2024 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package notifications + +import ( + "context" + "fmt" + "log/slog" + "slices" + "strings" + + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/api/client" + apidefaults "github.com/gravitational/teleport/api/defaults" + notificationsv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/notifications/v1" + "github.com/gravitational/teleport/api/internalutils/stream" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/authz" + "github.com/gravitational/teleport/lib/services" +) + +// ServiceConfig holds configuration options for the notifications gRPC service. +type ServiceConfig struct { + // Backend is the interface containing all methods for backend read/writes. + Backend Backend + + // Authorizer is the authorizer used to check access to resources. + Authorizer authz.Authorizer + + // UserNotificationCache is a custom cache for user-specific notifications, + // this is to allow fetching notifications by date in descending order. + UserNotificationCache *services.UserNotificationCache + + // GlobalNotificationCache is a custom cache for user-specific notifications, + // this is to allow fetching notifications by date in descending order. + GlobalNotificationCache *services.GlobalNotificationCache +} + +// Backend contains the getters required for notification states and user last seen notifications, +// as well as the methods required by the ReviewPermissionChecker. +type Backend interface { + services.Notifications + // Needed by the ReviewPermissionChecker + services.UserLoginStatesGetter + services.UserGetter + services.RoleGetter + client.ListResourcesClient + GetRoles(ctx context.Context) ([]types.Role, error) + GetClusterName(opts ...services.MarshalOption) (types.ClusterName, error) +} + +// Service implements the teleport.notifications.v1.NotificationsService RPC Service. +type Service struct { + notificationsv1.UnimplementedNotificationServiceServer + + authorizer authz.Authorizer + backend Backend + userNotificationCache *services.UserNotificationCache + globalNotificationCache *services.GlobalNotificationCache +} + +// NewService returns a new notifications gRPC service. +func NewService(cfg ServiceConfig) (*Service, error) { + switch { + case cfg.Backend == nil: + return nil, trace.BadParameter("backend is required") + case cfg.Authorizer == nil: + return nil, trace.BadParameter("authorizer is required") + case cfg.UserNotificationCache == nil: + return nil, trace.BadParameter("user notification cache is required") + case cfg.GlobalNotificationCache == nil: + return nil, trace.BadParameter("global notification cache is required") + } + + return &Service{ + authorizer: cfg.Authorizer, + backend: cfg.Backend, + userNotificationCache: cfg.UserNotificationCache, + globalNotificationCache: cfg.GlobalNotificationCache, + }, nil +} + +// ListNotifications returns a paginated list of notifications which match the user. +func (s *Service) ListNotifications(ctx context.Context, req *notificationsv1.ListNotificationsRequest) (*notificationsv1.ListNotificationsResponse, error) { + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + username := authCtx.User.GetName() + + // Fetch all of the user's notification states. We do this upfront to filter out dismissed notifications. + notificationStatesMap := make(map[string]notificationsv1.NotificationState) + for startKey := ""; ; { + notificationStates, nextKey, err := s.backend.ListUserNotificationStates(ctx, username, apidefaults.DefaultChunkSize, startKey) + if err != nil { + return nil, trace.Wrap(err) + } + for _, notificationState := range notificationStates { + if notificationState.Spec != nil && notificationState.Status != nil { + notificationStatesMap[notificationState.Spec.NotificationId] = notificationState.Status.GetNotificationState() + } + } + if nextKey == "" { + break + } + startKey = nextKey + } + + var userNotifMatchFn = func(n *notificationsv1.Notification) bool { + // Return true if the user hasn't dismissed this notification + return notificationStatesMap[n.GetMetadata().GetName()] != notificationsv1.NotificationState_NOTIFICATION_STATE_DISMISSED + } + userKey, globalKey, found := strings.Cut(req.PageToken, ",") + if !found && req.PageToken != "" { + return nil, trace.BadParameter("invalid page token provided") + } + pageSize := int(req.PageSize) + userNotifsStream := stream.Slice[*notificationsv1.Notification](nil) + if userKey != "" || !found { + userNotifsStream = stream.FilterMap( + s.userNotificationCache.StreamUserNotifications(ctx, username, userKey), + func(n *notificationsv1.Notification) (*notificationsv1.Notification, bool) { + if !userNotifMatchFn(n) { + return nil, false + } + return n, true + }) + } + globalNotifsStream := stream.Slice[*notificationsv1.GlobalNotification](nil) + if globalKey != "" || !found { + globalNotifsStream = stream.FilterMap( + s.globalNotificationCache.StreamGlobalNotifications(ctx, globalKey), + func(gn *notificationsv1.GlobalNotification) (*notificationsv1.GlobalNotification, bool) { + if !s.matchGlobalNotification(ctx, authCtx, gn, notificationStatesMap) { + return nil, false + } + return gn, true + }) + } + notifStream := stream.MergeStreams( + userNotifsStream, + globalNotifsStream, + isMoreRecent, + func(notification *notificationsv1.Notification) *notificationsv1.Notification { + return notification + }, + func(globalNotification *notificationsv1.GlobalNotification) *notificationsv1.Notification { + notification := globalNotification.GetSpec().GetNotification() + notification.Metadata.Name = globalNotification.GetMetadata().GetName() + return notification + }, + ) + var notifications []*notificationsv1.Notification + var nextGlobalKey, nextUserKey string + for notifStream.Next() { + item := notifStream.Item() + if item != nil { + notifications = append(notifications, item) + } + if len(notifications) == pageSize { + // The nextKeys should represent the next unconsumed items in their respective lists. + // If the last item in this page (ie. the current item in the stream) was a user-specific notification, then the userNotificationsNextKey should be the next item in the userNotifsStream, and + // the globalNotificationsNextKey should be the current (and unconsumed) item in the globalNotifsStream. And vice-versa. + if item.GetMetadata().GetLabels()[types.NotificationScope] == "user" { + nextGlobalKey = globalNotifsStream.Item().GetMetadata().GetName() + // Advance to the next user-specific notification. + ok := userNotifsStream.Next() + if ok { + // If it exists, set it as the userNotificationsNextKey, otherwise set it to "" + nextUserKey = userNotifsStream.Item().GetMetadata().GetName() + } else { + nextUserKey = "" + } + } else { + nextUserKey = userNotifsStream.Item().GetMetadata().GetName() + // Advance to the next global notification. + ok := globalNotifsStream.Next() + if ok { + // If it exists, set it as the globalNotificationsNextKey. + nextGlobalKey = globalNotifsStream.Item().GetMetadata().GetName() + } else { + nextGlobalKey = "" + } + } + break + } + } + userLastSeenNotification, err := s.backend.GetUserLastSeenNotification(ctx, username) + if err != nil && !trace.IsNotFound(err) { + return nil, trace.Wrap(err) + } + // Add label to indicate notifications that the user has clicked. + for _, notification := range notifications { + if (notificationStatesMap[notification.GetMetadata().GetName()]) == notificationsv1.NotificationState_NOTIFICATION_STATE_CLICKED { + notification.GetMetadata().Labels[types.NotificationClickedLabel] = "true" + } + } + var nextPageToken string + if nextUserKey != "" || nextGlobalKey != "" { + nextPageToken = fmt.Sprintf("%s,%s", nextUserKey, nextGlobalKey) + } + response := ¬ificationsv1.ListNotificationsResponse{ + Notifications: notifications, + NextPageToken: nextPageToken, + UserLastSeenNotificationTimestamp: userLastSeenNotification.GetStatus().GetLastSeenTime(), + } + return response, nil +} + +func (s *Service) matchGlobalNotification(ctx context.Context, authCtx *authz.Context, gn *notificationsv1.GlobalNotification, notificationStatesMap map[string]notificationsv1.NotificationState) bool { + // If the user has dismissed this notification, return false. + if notificationStatesMap[gn.GetMetadata().GetName()] == notificationsv1.NotificationState_NOTIFICATION_STATE_DISMISSED { + return false + } + + switch matcher := gn.Spec.Matcher.(type) { + case *notificationsv1.GlobalNotificationSpec_All: + // Always return true if the matcher is "all." + return true + + case *notificationsv1.GlobalNotificationSpec_ByRoles: + matcherRoles := matcher.ByRoles.GetRoles() + userRoles := authCtx.User.GetRoles() + + // If MatchAllConditions is true, then userRoles must contain every role in matcherRoles. + if gn.Spec.MatchAllConditions { + for _, matcherRole := range matcherRoles { + // Return false if there is any role missing. + if !slices.Contains(userRoles, matcherRole) { + return false + } + } + return true + } + + // Return true if it matches at least one matcherRole. + for _, matcherRole := range matcherRoles { + if slices.Contains(userRoles, matcherRole) { + return true + } + } + + return false + + case *notificationsv1.GlobalNotificationSpec_ByPermissions: + roleConditionsList := matcher.ByPermissions.GetRoleConditions() + + var results []bool + for _, roleConditions := range roleConditionsList { + match, err := s.matchRoleConditions(ctx, authCtx, roleConditions) + if err != nil { + slog.WarnContext(ctx, "Encountered error while matching RoleConditions", "role_conditions", roleConditions, "error", err) + return false + } + + // If MatchAllConditions is false, we can exit at the first match. + if !gn.Spec.MatchAllConditions && match { + return true + } + + // If MatchAllConditions is true, we exit at the first non-match. + if gn.Spec.MatchAllConditions && !match { + return false + } + + results = append(results, match) + } + + // Return false if any of the roleConditions didn't match. + if gn.Spec.MatchAllConditions { + return !slices.Contains(results, false) + } + + return false + } + + return false +} + +func (s *Service) matchRoleConditions(ctx context.Context, authCtx *authz.Context, rc *types.RoleConditions) (bool, error) { + if len(rc.Logins) > 0 { + userLogins := authCtx.Checker.GetAllLogins() + var matchedLogin bool + for _, login := range rc.Logins { + // If at least one of the logins match, this is a match. + if slices.Contains(userLogins, login) { + matchedLogin = true + break + } + } + if !matchedLogin { + return false, nil + } + } + + if len(rc.Rules) > 0 { + var matchedRule bool + for _, rule := range rc.Rules { + hasAccess, err := checkAccessToRule(authCtx, rule) + if err != nil { + return false, trace.Wrap(err, "encountered unexpected error when checking access to rule") + } + + // If the user has permissions for at least one of the rules, this is a match. + if hasAccess { + matchedRule = true + break + } + } + if !matchedRule { + return false, nil + } + } + + if rc.ReviewRequests != nil { + identity := authCtx.Identity.GetIdentity() + checker, err := services.NewReviewPermissionChecker( + ctx, + s.backend, + authCtx.User.GetName(), + &identity, + ) + if err != nil { + return false, trace.Wrap(err) + } + + // unless the user has allow directives for reviewing, they will never be able to + // see any requests other than their own. + if !checker.HasAllowDirectives() { + return false, nil + } + + // We instantiate a fake access request with the defined roles, this allows us to use our existing AccessReviewChecker to check if the + // user is allowed to review requests for them. + fakeAccessRequest, err := types.NewAccessRequest("fake", "fake", rc.ReviewRequests.Roles...) + if err != nil { + return false, trace.Wrap(err) + } + + canReview, err := checker.CanReviewRequest(fakeAccessRequest) + if err != nil { + return false, trace.Wrap(err, "failed to evaluate request review permissions") + } + + if !canReview { + return false, nil + } + } + + // This RoleConditions object matches if there were no failed matches that returned prior to this. + return true, nil +} + +// checkAccessToRule returns true if the user has the permissions defined in a rule. +func checkAccessToRule(authCtx *authz.Context, rule types.Rule) (bool, error) { + for _, resourceKind := range rule.Resources { + for _, verb := range rule.Verbs { + if err := authCtx.CheckAccessToKind(resourceKind, verb); err != nil { + // If the user doesn't have access for the verbs for any one of the resources in the rule, then just return false. + if trace.IsAccessDenied(err) { + return false, nil + // If the error is due to something else, then return it. + } else { + return false, trace.Wrap(err) + } + } + } + } + + return true, nil +} + +// isMoreRecent returns true if the userNotification is more recent than the globalNotification. +func isMoreRecent(userNotification *notificationsv1.Notification, globalNotification *notificationsv1.GlobalNotification) bool { + userNotificationTime := userNotification.GetSpec().GetCreated().AsTime() + globalNotificationTime := globalNotification.GetSpec().GetNotification().GetSpec().GetCreated().AsTime() + + return userNotificationTime.After(globalNotificationTime) +} + +// UpsertUserNotificationState creates or updates a user notification state which records whether the user has clicked on or dismissed a notification. +func (s *Service) UpsertUserNotificationState(ctx context.Context, req *notificationsv1.UpsertUserNotificationStateRequest) (*notificationsv1.UserNotificationState, error) { + if req.Username == "" { + return nil, trace.BadParameter("missing username") + } + if req.UserNotificationState == nil { + return nil, trace.BadParameter("missing notification state") + } + + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + username := authCtx.User.GetName() + if username != req.Username { + return nil, trace.AccessDenied("a user may only update their own notification state") + } + + out, err := s.backend.UpsertUserNotificationState(ctx, req.Username, req.UserNotificationState) + if err != nil { + return nil, trace.Wrap(err) + } + + return out, nil +} + +// UpsertUserLastSeenNotification creates or updates a user's last seen notification timestamp. +func (s *Service) UpsertUserLastSeenNotification(ctx context.Context, req *notificationsv1.UpsertUserLastSeenNotificationRequest) (*notificationsv1.UserLastSeenNotification, error) { + if req.Username == "" { + return nil, trace.BadParameter("missing username") + } + if req.UserLastSeenNotification == nil { + return nil, trace.BadParameter("missing user last seen notification") + } + + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + username := authCtx.User.GetName() + if username != req.Username { + return nil, trace.AccessDenied("a user may only update their own last seen notification timestamp") + } + + out, err := s.backend.UpsertUserLastSeenNotification(ctx, req.Username, req.UserLastSeenNotification) + if err != nil { + return nil, trace.Wrap(err) + } + + return out, nil +} diff --git a/lib/cache/cache_test.go b/lib/cache/cache_test.go index 71379611e0d..ef62f812f33 100644 --- a/lib/cache/cache_test.go +++ b/lib/cache/cache_test.go @@ -3626,14 +3626,14 @@ func newUserNotification(t *testing.T, name string) *notificationsv1.Notificatio Username: name, }, Metadata: &headerv1.Metadata{ - Labels: map[string]string{"description": "test-description"}, + Labels: map[string]string{types.NotificationTitleLabel: "test-title"}, }, } return notification } -func newGlobalNotification(t *testing.T, description string) *notificationsv1.GlobalNotification { +func newGlobalNotification(t *testing.T, title string) *notificationsv1.GlobalNotification { t.Helper() notification := ¬ificationsv1.GlobalNotification{ @@ -3645,7 +3645,7 @@ func newGlobalNotification(t *testing.T, description string) *notificationsv1.Gl SubKind: "test-subkind", Spec: ¬ificationsv1.NotificationSpec{}, Metadata: &headerv1.Metadata{ - Labels: map[string]string{"description": description}, + Labels: map[string]string{types.NotificationTitleLabel: title}, }, }, }, diff --git a/lib/cache/collections.go b/lib/cache/collections.go index c8d716b433d..339a014cdf4 100644 --- a/lib/cache/collections.go +++ b/lib/cache/collections.go @@ -3044,7 +3044,7 @@ func (userNotificationExecutor) delete(ctx context.Context, cache *Cache, resour } username := notification.GetSpec().GetUsername() - notificationId := notification.GetSpec().GetId() + notificationId := notification.GetMetadata().GetName() err := cache.notificationsCache.DeleteUserNotification(ctx, username, notificationId) return trace.Wrap(err) @@ -3107,7 +3107,7 @@ func (globalNotificationExecutor) delete(ctx context.Context, cache *Cache, reso return trace.BadParameter("unknown Notification type, expected *notificationsv1.GlobalNotification, got %T", resource) } - notificationId := globalNotification.GetSpec().GetNotification().GetSpec().GetId() + notificationId := globalNotification.GetMetadata().GetName() err := cache.notificationsCache.DeleteGlobalNotification(ctx, notificationId) return trace.Wrap(err) diff --git a/lib/services/local/events.go b/lib/services/local/events.go index 9d0f04ef4c6..fa3b1d05d25 100644 --- a/lib/services/local/events.go +++ b/lib/services/local/events.go @@ -2049,7 +2049,6 @@ func (p *userNotificationParser) parse(event backend.Event) (types.Resource, err Version: types.V1, Spec: ¬ificationsv1.NotificationSpec{ Username: parts[2], - Id: parts[3], }, Metadata: &headerv1.Metadata{ Name: parts[3], @@ -2100,9 +2099,7 @@ func (p *globalNotificationParser) parse(event backend.Event) (types.Resource, e Version: types.V1, Spec: ¬ificationsv1.GlobalNotificationSpec{ Notification: ¬ificationsv1.Notification{ - Spec: ¬ificationsv1.NotificationSpec{ - Id: parts[2], - }, + Spec: ¬ificationsv1.NotificationSpec{}, }, }, Metadata: &headerv1.Metadata{ diff --git a/lib/services/local/notifications.go b/lib/services/local/notifications.go index d20726b2a5a..3fe337ac532 100644 --- a/lib/services/local/notifications.go +++ b/lib/services/local/notifications.go @@ -140,10 +140,7 @@ func (s *NotificationsService) CreateUserNotification(ctx context.Context, notif if err != nil { return nil, trace.Wrap(err) } - notification.Spec.Id = uuid.String() - - // We set this to the UUID because the service adapter uses `getName()` to determine the backend key to use when storing the notification. - notification.Metadata.Name = notification.Spec.Id + notification.Metadata.Name = uuid.String() if err := CheckAndSetExpiry(notification, s.clock); err != nil { return nil, trace.Wrap(err) @@ -151,6 +148,8 @@ func (s *NotificationsService) CreateUserNotification(ctx context.Context, notif notification.Spec.Created = timestamppb.New(s.clock.Now()) + notification.Metadata.Labels[types.NotificationScope] = "user" + // Append username prefix. serviceWithPrefix := s.userNotificationService.WithPrefix(notification.Spec.Username) @@ -165,7 +164,7 @@ func (s *NotificationsService) UpsertUserNotification(ctx context.Context, notif } // Precautionary check in case of accidental misuse. - if notification.Spec.Id == "" { + if notification.Metadata.Name == "" { return nil, trace.BadParameter("notification id is missing. Did you mean to use CreateUserNotification?") } @@ -230,10 +229,8 @@ func (s *NotificationsService) CreateGlobalNotification(ctx context.Context, glo if err != nil { return nil, trace.Wrap(err) } - globalNotification.Spec.Notification.Spec.Id = uuid.String() - - // We set this to the UUID because the service adapter uses `getName()` to determine the backend key to use when storing the notification. - globalNotification.Metadata = &headerv1.Metadata{Name: globalNotification.Spec.Notification.Spec.Id} + globalNotification.Metadata = &headerv1.Metadata{Name: uuid.String()} + globalNotification.Spec.Notification.Metadata.Name = uuid.String() if err := CheckAndSetExpiry(globalNotification.Spec.Notification, s.clock); err != nil { return nil, trace.Wrap(err) @@ -241,6 +238,8 @@ func (s *NotificationsService) CreateGlobalNotification(ctx context.Context, glo globalNotification.Spec.Notification.Spec.Created = timestamppb.New(s.clock.Now()) + globalNotification.Spec.Notification.Metadata.Labels[types.NotificationScope] = "global" + created, err := s.globalNotificationService.CreateResource(ctx, globalNotification) return created, trace.Wrap(err) } @@ -252,7 +251,7 @@ func (s *NotificationsService) UpsertGlobalNotification(ctx context.Context, glo } // Precautionary check in case of accidental misuse. - if globalNotification.Spec.Notification.Spec.Id == "" { + if globalNotification.Spec.Notification.Metadata.Name == "" { return nil, trace.BadParameter("notification id is missing. Did you mean to use CreateGlobalNotification?") } @@ -274,8 +273,21 @@ func (s *NotificationsService) UpsertUserNotificationState(ctx context.Context, // Verify that the notification this state is for exists. notifServiceWithPrefix := s.userNotificationService.WithPrefix(username) - if _, err := notifServiceWithPrefix.GetResource(ctx, state.Spec.NotificationId); err != nil { - return nil, trace.Wrap(err) + _, err := notifServiceWithPrefix.GetResource(ctx, state.Spec.NotificationId) + if err != nil { + if !trace.IsNotFound(err) { + return nil, trace.Wrap(err) + } + + // If we didn't find a user-specific notification with this ID, try finding a global notification. + _, err := s.globalNotificationService.GetResource(ctx, state.Spec.NotificationId) + if err != nil { + if !trace.IsNotFound(err) { + return nil, trace.Wrap(err) + } + + return nil, trace.NotFound("notification '%s' does not exist", state.Spec.NotificationId) + } } state.Kind = types.KindUserNotificationState diff --git a/lib/services/local/notifications_test.go b/lib/services/local/notifications_test.go index 64d57115821..44501e73c27 100644 --- a/lib/services/local/notifications_test.go +++ b/lib/services/local/notifications_test.go @@ -32,6 +32,7 @@ import ( headerv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" notificationsv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/notifications/v1" + "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/backend" "github.com/gravitational/teleport/lib/backend/memory" ) @@ -60,14 +61,14 @@ func TestUserNotificationCRUD(t *testing.T) { notification, err := service.CreateUserNotification(ctx, userNotification1) require.NoError(t, err) require.Empty(t, cmp.Diff(userNotification1, notification, protocmp.Transform())) - notification1Id := notification.Spec.Id + notification1Id := notification.GetMetadata().GetName() // Prevent flakiness caused by notifications being created too close one after the other, which causes their UUID timestamps to be the same // and the lexicographical ordering to possibly be wrong as it then relies on the random section of the UUID. time.Sleep(250 * time.Millisecond) notification, err = service.CreateUserNotification(ctx, userNotification2) require.NoError(t, err) require.Empty(t, cmp.Diff(userNotification2, notification, protocmp.Transform())) - notification2Id := notification.Spec.Id + notification2Id := notification.GetMetadata().GetName() // Test deleting a notification. err = service.DeleteUserNotification(ctx, testUsername, notification1Id) @@ -87,7 +88,7 @@ func TestUserNotificationCRUD(t *testing.T) { userNotification1 = newUserNotification(t, testUsername, "test-notification-1") _, err = service.CreateUserNotification(ctx, userNotification1) require.NoError(t, err) - notification1Id = notification.Spec.Id + notification1Id = notification.GetMetadata().GetName() err = service.DeleteAllUserNotificationsForUser(ctx, testUsername) require.NoError(t, err) // Verify that the notifications don't exist anymore by attempting to delete them. @@ -148,7 +149,7 @@ func TestGlobalNotificationCRUD(t *testing.T) { notification, err := service.CreateGlobalNotification(ctx, globalNotification1) require.NoError(t, err) require.Empty(t, cmp.Diff(globalNotification1, notification, protocmp.Transform())) - globalNotification1Id := notification.Spec.Notification.Spec.Id + globalNotification1Id := notification.GetMetadata().GetName() notification, err = service.CreateGlobalNotification(ctx, globalNotification2) require.NoError(t, err) require.Empty(t, cmp.Diff(globalNotification2, notification, protocmp.Transform())) @@ -188,13 +189,13 @@ func TestUserNotificationStateCRUD(t *testing.T) { userNotification2 := newUserNotification(t, testUsername, "test-notification-2") notification, err := service.CreateUserNotification(ctx, userNotification1) require.NoError(t, err) - notification1Id := notification.Spec.Id + notification1Id := notification.GetMetadata().GetName() // Prevent flakiness caused by notifications being created too close one after the other, which causes their UUID timestamps to be the same // and the lexicographical ordering to possibly be wrong as it then relies on the random section of the UUID. time.Sleep(250 * time.Millisecond) notification, err = service.CreateUserNotification(ctx, userNotification2) require.NoError(t, err) - notification2Id := notification.Spec.Id + notification2Id := notification.GetMetadata().GetName() userNotificationState1 := ¬ificationsv1.UserNotificationState{ Spec: ¬ificationsv1.UserNotificationStateSpec{ @@ -374,7 +375,7 @@ func TestUserLastSeenNotificationCRUD(t *testing.T) { require.True(t, trace.IsNotFound(err), "got error %T, expected a not found error due to user_last_seen_notification for test-username not existing", err) } -func newUserNotification(t *testing.T, username string, description string) *notificationsv1.Notification { +func newUserNotification(t *testing.T, username string, title string) *notificationsv1.Notification { t.Helper() notification := notificationsv1.Notification{ @@ -383,14 +384,16 @@ func newUserNotification(t *testing.T, username string, description string) *not Username: username, }, Metadata: &headerv1.Metadata{ - Labels: map[string]string{"description": description}, + Labels: map[string]string{ + types.NotificationTitleLabel: title, + }, }, } return ¬ification } -func newGlobalNotification(t *testing.T, description string) *notificationsv1.GlobalNotification { +func newGlobalNotification(t *testing.T, title string) *notificationsv1.GlobalNotification { t.Helper() notification := notificationsv1.GlobalNotification{ @@ -402,7 +405,9 @@ func newGlobalNotification(t *testing.T, description string) *notificationsv1.Gl SubKind: "test-subkind", Spec: ¬ificationsv1.NotificationSpec{}, Metadata: &headerv1.Metadata{ - Labels: map[string]string{"description": description}, + Labels: map[string]string{ + types.NotificationTitleLabel: title, + }, }, }, }, diff --git a/lib/services/notifications.go b/lib/services/notifications.go index ea2bb28d878..c3b094ea2bb 100644 --- a/lib/services/notifications.go +++ b/lib/services/notifications.go @@ -24,6 +24,7 @@ import ( "github.com/gravitational/trace" notificationsv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/notifications/v1" + "github.com/gravitational/teleport/api/types" ) // Notifications defines an interface for managing notifications. @@ -62,8 +63,8 @@ func ValidateNotification(notification *notificationsv1.Notification) error { return trace.BadParameter("notification metadata is missing") } - if notification.Metadata.Labels == nil { - return trace.BadParameter("notification metadata labels are missing") + if _, exists := notification.Metadata.GetLabels()[types.NotificationTitleLabel]; !exists { + return trace.BadParameter("notification title label is missing") } return nil diff --git a/lib/services/notifications_cache.go b/lib/services/notifications_cache.go index 8b6a70c3c9a..5a4f2e75a99 100644 --- a/lib/services/notifications_cache.go +++ b/lib/services/notifications_cache.go @@ -205,6 +205,9 @@ func (c *UserNotificationCache) StreamUserNotifications(ctx context.Context, use // Get the initial startKey if it wasn't provided. if startKey == "" { startKey = sortcache.NextKey(endKey) + } else { + // The sortcache expects the key to be in / format, so we prepend the username since the startKey passed into this function will just be a UUID. + startKey = fmt.Sprintf("%s/%s", username, startKey) } var done bool @@ -266,7 +269,7 @@ func (c *UserNotificationCache) fetch(ctx context.Context) (*sortcache.SortCache // GetUserSpecificKey returns the key for a user-specific notification in / format. func GetUserSpecificKey(n *notificationsv1.Notification) string { username := n.GetSpec().GetUsername() - id := n.GetSpec().GetId() + id := n.GetMetadata().GetName() return fmt.Sprintf("%s/%s", username, id) } diff --git a/lib/services/notifications_cache_test.go b/lib/services/notifications_cache_test.go index 91f630ea7c1..a83b767a454 100644 --- a/lib/services/notifications_cache_test.go +++ b/lib/services/notifications_cache_test.go @@ -109,50 +109,50 @@ func TestUserNotificationsCache(t *testing.T) { usernameB := "bob" // Describe a set of mock user notifications to be created. - // The number in the description represents the order in which they are created, eg. alice-1 is the first (and thus oldest) notification to be created for alice. + // The number in the title represents the order in which they are created, eg. alice-1 is the first (and thus oldest) notification to be created for alice. testNotifications := []struct { - username string - description string + username string + title string }{ { - username: usernameB, - description: "bob-1", + username: usernameB, + title: "bob-1", }, { - username: usernameB, - description: "bob-2", + username: usernameB, + title: "bob-2", }, { - username: usernameA, - description: "alice-1", + username: usernameA, + title: "alice-1", }, { - username: usernameB, - description: "bob-3", + username: usernameB, + title: "bob-3", }, { - username: usernameB, - description: "bob-4", + username: usernameB, + title: "bob-4", }, { - username: usernameA, - description: "alice-2", + username: usernameA, + title: "alice-2", }, { - username: usernameA, - description: "alice-3", + username: usernameA, + title: "alice-3", }, { - username: usernameA, - description: "alice-4", + username: usernameA, + title: "alice-4", }, { - username: usernameB, - description: "bob-5", + username: usernameB, + title: "bob-5", }, { - username: usernameA, - description: "alice-5", + username: usernameA, + title: "alice-5", }, } @@ -164,7 +164,7 @@ func TestUserNotificationsCache(t *testing.T) { for _, n := range testNotifications { // We add a delay to ensure that the timestamps in the generated UUID's are all different and in the correct order. time.Sleep(50 * time.Millisecond) - notification := newUserNotification(t, n.username, n.description) + notification := newUserNotification(t, n.username, n.title) // Create the notification in the backend. created, err := svcs.CreateUserNotification(ctx, notification) require.NoError(t, err) @@ -205,41 +205,41 @@ func TestUserNotificationsCache(t *testing.T) { streamA.Next() streamA.Next() streamA.Next() - usernameAThirdItemStartKey = services.GetUserSpecificKey(streamA.Item()) + usernameAThirdItemStartKey = streamA.Item().GetMetadata().GetName() var usernameBThirdItemStartKey string streamB := cache.StreamUserNotifications(ctx, usernameB, "") streamB.Next() streamB.Next() streamB.Next() - usernameBThirdItemStartKey = services.GetUserSpecificKey(streamB.Item()) + usernameBThirdItemStartKey = streamB.Item().GetMetadata().GetName() testCases := []struct { - testName string - username string - startKey string - expectedNotificationDescriptions []string + testName string + username string + startKey string + expectedNotificationTitles []string }{ { testName: "correctly fetches sorted notifications for usernameA with no startKey", username: usernameA, // Since alice-1 is the oldest, it should be at the end of the list. - expectedNotificationDescriptions: []string{"alice-5", "alice-4", "alice-3", "alice-2", "alice-1"}, + expectedNotificationTitles: []string{"alice-5", "alice-4", "alice-3", "alice-2", "alice-1"}, }, { - testName: "correctly fetches sorted notifications for usernameB with no startKey", - username: usernameB, - expectedNotificationDescriptions: []string{"bob-5", "bob-4", "bob-3", "bob-2", "bob-1"}, + testName: "correctly fetches sorted notifications for usernameB with no startKey", + username: usernameB, + expectedNotificationTitles: []string{"bob-5", "bob-4", "bob-3", "bob-2", "bob-1"}, }, { - testName: "correctly fetches sorted notifications for usernameA with a startKey", - username: usernameA, - startKey: usernameAThirdItemStartKey, - expectedNotificationDescriptions: []string{"alice-3", "alice-2", "alice-1"}, + testName: "correctly fetches sorted notifications for usernameA with a startKey", + username: usernameA, + startKey: usernameAThirdItemStartKey, + expectedNotificationTitles: []string{"alice-3", "alice-2", "alice-1"}, }, { - testName: "correctly fetches sorted notifications for usernameB with a startKey", - username: usernameB, - startKey: usernameBThirdItemStartKey, - expectedNotificationDescriptions: []string{"bob-3", "bob-2", "bob-1"}, + testName: "correctly fetches sorted notifications for usernameB with a startKey", + username: usernameB, + startKey: usernameBThirdItemStartKey, + expectedNotificationTitles: []string{"bob-3", "bob-2", "bob-1"}, }, } @@ -248,11 +248,11 @@ func TestUserNotificationsCache(t *testing.T) { var out []string notifsStream := cache.StreamUserNotifications(ctx, tc.username, tc.startKey) for notifsStream.Next() { - desc := notifsStream.Item().GetMetadata().GetLabels()["description"] - out = append(out, desc) + title := notifsStream.Item().GetMetadata().GetLabels()[types.NotificationTitleLabel] + out = append(out, title) } notifsStream.Done() - require.Equal(t, tc.expectedNotificationDescriptions, out) + require.Equal(t, tc.expectedNotificationTitles, out) }) } @@ -303,8 +303,8 @@ func TestUserNotificationsCache(t *testing.T) { expected := []string{"alice-3", "alice-2", "alice-1"} notifsStream := cache.StreamUserNotifications(ctx, usernameA, "") for notifsStream.Next() { - desc := notifsStream.Item().GetMetadata().GetLabels()["description"] - out = append(out, desc) + title := notifsStream.Item().GetMetadata().GetLabels()[types.NotificationTitleLabel] + out = append(out, title) } notifsStream.Done() require.Equal(t, expected, out) @@ -320,7 +320,7 @@ func TestGlobalNotificationsCache(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - testNotificationDescriptions := []string{ + testNotificationTitles := []string{ "gn-1", "gn-2", "gn-3", @@ -332,7 +332,7 @@ func TestGlobalNotificationsCache(t *testing.T) { var notificationIds []string // Create the notifications, with a 50ms delay between each one. - for _, gn := range testNotificationDescriptions { + for _, gn := range testNotificationTitles { time.Sleep(50 * time.Millisecond) notification := newGlobalNotification(t, gn) // Create the notification in the backend. @@ -350,7 +350,7 @@ func TestGlobalNotificationsCache(t *testing.T) { require.NoError(t, err) gnStream.Done() - if len(collectedStream) == len(testNotificationDescriptions) { + if len(collectedStream) == len(testNotificationTitles) { break } @@ -375,8 +375,8 @@ func TestGlobalNotificationsCache(t *testing.T) { expected := []string{"gn-5", "gn-4", "gn-3", "gn-2", "gn-1"} gnStream = cache.StreamGlobalNotifications(ctx, "") for gnStream.Next() { - desc := gnStream.Item().GetSpec().GetNotification().GetMetadata().GetLabels()["description"] - out = append(out, desc) + title := gnStream.Item().GetSpec().GetNotification().GetMetadata().GetLabels()[types.NotificationTitleLabel] + out = append(out, title) } require.Equal(t, expected, out) gnStream.Done() @@ -386,8 +386,8 @@ func TestGlobalNotificationsCache(t *testing.T) { expected = []string{"gn-3", "gn-2", "gn-1"} gnStream = cache.StreamGlobalNotifications(ctx, thirdItemStartKey) for gnStream.Next() { - desc := gnStream.Item().GetSpec().GetNotification().GetMetadata().GetLabels()["description"] - out = append(out, desc) + title := gnStream.Item().GetSpec().GetNotification().GetMetadata().GetLabels()[types.NotificationTitleLabel] + out = append(out, title) } require.Equal(t, expected, out) gnStream.Done() @@ -420,7 +420,7 @@ func TestGlobalNotificationsCache(t *testing.T) { } } -func newUserNotification(t *testing.T, username string, description string) *notificationsv1.Notification { +func newUserNotification(t *testing.T, username string, title string) *notificationsv1.Notification { t.Helper() notification := notificationsv1.Notification{ @@ -429,14 +429,16 @@ func newUserNotification(t *testing.T, username string, description string) *not Username: username, }, Metadata: &headerv1.Metadata{ - Labels: map[string]string{"description": description}, + Labels: map[string]string{ + types.NotificationTitleLabel: title, + }, }, } return ¬ification } -func newGlobalNotification(t *testing.T, description string) *notificationsv1.GlobalNotification { +func newGlobalNotification(t *testing.T, title string) *notificationsv1.GlobalNotification { t.Helper() notification := notificationsv1.GlobalNotification{ @@ -448,7 +450,9 @@ func newGlobalNotification(t *testing.T, description string) *notificationsv1.Gl SubKind: "test-subkind", Spec: ¬ificationsv1.NotificationSpec{}, Metadata: &headerv1.Metadata{ - Labels: map[string]string{"description": description}, + Labels: map[string]string{ + types.NotificationTitleLabel: title, + }, }, }, }, diff --git a/lib/services/notifications_test.go b/lib/services/notifications_test.go index 70e2c264663..e199a928c20 100644 --- a/lib/services/notifications_test.go +++ b/lib/services/notifications_test.go @@ -38,11 +38,10 @@ func TestMarshalNotificationRoundTrip(t *testing.T) { Kind: types.KindNotification, Version: types.V1, SubKind: "test-subkind", - Spec: ¬ificationsv1.NotificationSpec{ - Id: "test-notification-1", - }, + Spec: ¬ificationsv1.NotificationSpec{}, Metadata: &headerv1.Metadata{ - Labels: map[string]string{"description": "description-1"}, + Name: "test-notification-1", + Labels: map[string]string{types.NotificationTitleLabel: "title-1"}, }, } @@ -67,11 +66,10 @@ func TestMarshalGlobalNotificationRoundTrip(t *testing.T) { }, Notification: ¬ificationsv1.Notification{ SubKind: "test-subkind", - Spec: ¬ificationsv1.NotificationSpec{ - Id: "test-notification-id", - }, + Spec: ¬ificationsv1.NotificationSpec{}, Metadata: &headerv1.Metadata{ - Labels: map[string]string{"description": "description-1"}, + Name: "test-notification-id", + Labels: map[string]string{types.NotificationTitleLabel: "title-1"}, }, }, },