From fdf95dc5c5c92170aed4a26872987168895eee72 Mon Sep 17 00:00:00 2001 From: Yassine Bounekhla Date: Tue, 20 Feb 2024 15:14:06 -0500 Subject: [PATCH] add notifications backend service --- .../notifications/v1/notifications.pb.go | 127 +-- .../v1/notifications_service.pb.go | 779 ++++++------------ .../v1/notifications_service_grpc.pb.go | 221 ++--- .../notifications/v1/notifications.proto | 14 +- .../v1/notifications_service.proto | 57 +- api/types/constants.go | 9 + lib/services/local/generic/generic_wrapper.go | 25 + .../local/generic/generic_wrapper_test.go | 28 + lib/services/local/notifications.go | 309 +++++++ lib/services/local/notifications_test.go | 404 +++++++++ lib/services/notifications.go | 307 +++++++ lib/services/notifications_test.go | 127 +++ 12 files changed, 1598 insertions(+), 809 deletions(-) create mode 100644 lib/services/local/notifications.go create mode 100644 lib/services/local/notifications_test.go create mode 100644 lib/services/notifications.go create mode 100644 lib/services/notifications_test.go 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 9c8c289ba803e..edaa7c59b0fcc 100644 --- a/api/gen/proto/go/teleport/notifications/v1/notifications.pb.go +++ b/api/gen/proto/go/teleport/notifications/v1/notifications.pb.go @@ -1,6 +1,6 @@ // // Teleport -// Copyright (C) 2023 Gravitational, Inc. +// 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 @@ -104,7 +104,7 @@ type Notification struct { SubKind string `protobuf:"bytes,2,opt,name=sub_kind,json=subKind,proto3" json:"sub_kind,omitempty"` // version is the resource version. Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` - // metadata is the notification's metadata. This contains the notification's title, description, labels, and expiry. + // metadata is the notification's metadata. This contains the notification's labels, and expiry. All custom notification metadata should be stored in labels. Metadata *v1.Metadata `protobuf:"bytes,4,opt,name=metadata,proto3" json:"metadata,omitempty"` // spec is the notification specification. Spec *NotificationSpec `protobuf:"bytes,5,opt,name=spec,proto3" json:"spec,omitempty"` @@ -758,8 +758,8 @@ type UserLastSeenNotification struct { Metadata *v1.Metadata `protobuf:"bytes,4,opt,name=metadata,proto3" json:"metadata,omitempty"` // UserLastSeenNotificationSpec is the user last seen notification item's specification. Spec *UserLastSeenNotificationSpec `protobuf:"bytes,5,opt,name=spec,proto3" json:"spec,omitempty"` - // time is the timestamp of this user's last seen notification, it contains the timestamp of the notification which will be dynamically modified. - Time *UserLastSeenNotificationTime `protobuf:"bytes,6,opt,name=time,proto3" json:"time,omitempty"` + // status is the timestamp of this user's last seen notification, it contains the timestamp of the notification which will be dynamically modified. + Status *UserLastSeenNotificationStatus `protobuf:"bytes,7,opt,name=status,proto3" json:"status,omitempty"` } func (x *UserLastSeenNotification) Reset() { @@ -829,9 +829,9 @@ func (x *UserLastSeenNotification) GetSpec() *UserLastSeenNotificationSpec { return nil } -func (x *UserLastSeenNotification) GetTime() *UserLastSeenNotificationTime { +func (x *UserLastSeenNotification) GetStatus() *UserLastSeenNotificationStatus { if x != nil { - return x.Time + return x.Status } return nil } @@ -875,8 +875,8 @@ func (*UserLastSeenNotificationSpec) Descriptor() ([]byte, []int) { return file_teleport_notifications_v1_notifications_proto_rawDescGZIP(), []int{10} } -// UserLastSeenNotificationTime is the timestamp of this user's last seen notification, it contains the timestamp of the notification which will be dynamically modified. -type UserLastSeenNotificationTime struct { +// UserLastSeenNotificationStatus is the timestamp of this user's last seen notification, it contains the timestamp of the notification which will be dynamically modified. +type UserLastSeenNotificationStatus struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -885,8 +885,8 @@ type UserLastSeenNotificationTime struct { LastSeenTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=last_seen_time,json=lastSeenTime,proto3" json:"last_seen_time,omitempty"` } -func (x *UserLastSeenNotificationTime) Reset() { - *x = UserLastSeenNotificationTime{} +func (x *UserLastSeenNotificationStatus) Reset() { + *x = UserLastSeenNotificationStatus{} if protoimpl.UnsafeEnabled { mi := &file_teleport_notifications_v1_notifications_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -894,13 +894,13 @@ func (x *UserLastSeenNotificationTime) Reset() { } } -func (x *UserLastSeenNotificationTime) String() string { +func (x *UserLastSeenNotificationStatus) String() string { return protoimpl.X.MessageStringOf(x) } -func (*UserLastSeenNotificationTime) ProtoMessage() {} +func (*UserLastSeenNotificationStatus) ProtoMessage() {} -func (x *UserLastSeenNotificationTime) ProtoReflect() protoreflect.Message { +func (x *UserLastSeenNotificationStatus) ProtoReflect() protoreflect.Message { mi := &file_teleport_notifications_v1_notifications_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -912,12 +912,12 @@ func (x *UserLastSeenNotificationTime) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use UserLastSeenNotificationTime.ProtoReflect.Descriptor instead. -func (*UserLastSeenNotificationTime) Descriptor() ([]byte, []int) { +// Deprecated: Use UserLastSeenNotificationStatus.ProtoReflect.Descriptor instead. +func (*UserLastSeenNotificationStatus) Descriptor() ([]byte, []int) { return file_teleport_notifications_v1_notifications_proto_rawDescGZIP(), []int{11} } -func (x *UserLastSeenNotificationTime) GetLastSeenTime() *timestamppb.Timestamp { +func (x *UserLastSeenNotificationStatus) GetLastSeenTime() *timestamppb.Timestamp { if x != nil { return x.LastSeenTime } @@ -1032,7 +1032,7 @@ var file_teleport_notifications_v1_notifications_proto_rawDesc = []byte{ 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, 0xb7, 0x02, 0x0a, 0x18, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x61, + 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, @@ -1047,34 +1047,35 @@ var file_teleport_notifications_v1_notifications_proto_rawDesc = []byte{ 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, 0x4b, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 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, 0x54, 0x69, 0x6d, 0x65, 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, - 0x60, 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, 0x54, 0x69, 0x6d, 0x65, 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, + 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, 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, 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, + 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 ( @@ -1092,22 +1093,22 @@ func file_teleport_notifications_v1_notifications_proto_rawDescGZIP() []byte { var file_teleport_notifications_v1_notifications_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_teleport_notifications_v1_notifications_proto_msgTypes = make([]protoimpl.MessageInfo, 12) var file_teleport_notifications_v1_notifications_proto_goTypes = []interface{}{ - (NotificationState)(0), // 0: teleport.notifications.v1.NotificationState - (*Notification)(nil), // 1: teleport.notifications.v1.Notification - (*NotificationSpec)(nil), // 2: teleport.notifications.v1.NotificationSpec - (*GlobalNotification)(nil), // 3: teleport.notifications.v1.GlobalNotification - (*GlobalNotificationSpec)(nil), // 4: teleport.notifications.v1.GlobalNotificationSpec - (*ByPermissions)(nil), // 5: teleport.notifications.v1.ByPermissions - (*ByRoles)(nil), // 6: teleport.notifications.v1.ByRoles - (*UserNotificationState)(nil), // 7: teleport.notifications.v1.UserNotificationState - (*UserNotificationStateSpec)(nil), // 8: teleport.notifications.v1.UserNotificationStateSpec - (*UserNotificationStateStatus)(nil), // 9: teleport.notifications.v1.UserNotificationStateStatus - (*UserLastSeenNotification)(nil), // 10: teleport.notifications.v1.UserLastSeenNotification - (*UserLastSeenNotificationSpec)(nil), // 11: teleport.notifications.v1.UserLastSeenNotificationSpec - (*UserLastSeenNotificationTime)(nil), // 12: teleport.notifications.v1.UserLastSeenNotificationTime - (*v1.Metadata)(nil), // 13: teleport.header.v1.Metadata - (*timestamppb.Timestamp)(nil), // 14: google.protobuf.Timestamp - (*types.RoleConditions)(nil), // 15: types.RoleConditions + (NotificationState)(0), // 0: teleport.notifications.v1.NotificationState + (*Notification)(nil), // 1: teleport.notifications.v1.Notification + (*NotificationSpec)(nil), // 2: teleport.notifications.v1.NotificationSpec + (*GlobalNotification)(nil), // 3: teleport.notifications.v1.GlobalNotification + (*GlobalNotificationSpec)(nil), // 4: teleport.notifications.v1.GlobalNotificationSpec + (*ByPermissions)(nil), // 5: teleport.notifications.v1.ByPermissions + (*ByRoles)(nil), // 6: teleport.notifications.v1.ByRoles + (*UserNotificationState)(nil), // 7: teleport.notifications.v1.UserNotificationState + (*UserNotificationStateSpec)(nil), // 8: teleport.notifications.v1.UserNotificationStateSpec + (*UserNotificationStateStatus)(nil), // 9: teleport.notifications.v1.UserNotificationStateStatus + (*UserLastSeenNotification)(nil), // 10: teleport.notifications.v1.UserLastSeenNotification + (*UserLastSeenNotificationSpec)(nil), // 11: teleport.notifications.v1.UserLastSeenNotificationSpec + (*UserLastSeenNotificationStatus)(nil), // 12: teleport.notifications.v1.UserLastSeenNotificationStatus + (*v1.Metadata)(nil), // 13: teleport.header.v1.Metadata + (*timestamppb.Timestamp)(nil), // 14: google.protobuf.Timestamp + (*types.RoleConditions)(nil), // 15: types.RoleConditions } var file_teleport_notifications_v1_notifications_proto_depIdxs = []int32{ 13, // 0: teleport.notifications.v1.Notification.metadata:type_name -> teleport.header.v1.Metadata @@ -1125,8 +1126,8 @@ var file_teleport_notifications_v1_notifications_proto_depIdxs = []int32{ 0, // 12: teleport.notifications.v1.UserNotificationStateStatus.notification_state:type_name -> teleport.notifications.v1.NotificationState 13, // 13: teleport.notifications.v1.UserLastSeenNotification.metadata:type_name -> teleport.header.v1.Metadata 11, // 14: teleport.notifications.v1.UserLastSeenNotification.spec:type_name -> teleport.notifications.v1.UserLastSeenNotificationSpec - 12, // 15: teleport.notifications.v1.UserLastSeenNotification.time:type_name -> teleport.notifications.v1.UserLastSeenNotificationTime - 14, // 16: teleport.notifications.v1.UserLastSeenNotificationTime.last_seen_time:type_name -> google.protobuf.Timestamp + 12, // 15: teleport.notifications.v1.UserLastSeenNotification.status:type_name -> teleport.notifications.v1.UserLastSeenNotificationStatus + 14, // 16: teleport.notifications.v1.UserLastSeenNotificationStatus.last_seen_time:type_name -> google.protobuf.Timestamp 17, // [17:17] is the sub-list for method output_type 17, // [17:17] is the sub-list for method input_type 17, // [17:17] is the sub-list for extension type_name @@ -1273,7 +1274,7 @@ func file_teleport_notifications_v1_notifications_proto_init() { } } file_teleport_notifications_v1_notifications_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UserLastSeenNotificationTime); i { + switch v := v.(*UserLastSeenNotificationStatus); i { case 0: return &v.state case 1: 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 e785f5868693b..1d5da0a22dbf6 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 @@ -1,6 +1,6 @@ // // Teleport -// Copyright (C) 2023 Gravitational, Inc. +// 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 @@ -154,7 +154,7 @@ func (x *DeleteUserNotificationRequest) GetNotificationId() string { return "" } -// ListUserNotificationsRequest is the request for listing a user's user-specific notifications. +// 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 { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -377,124 +377,8 @@ func (x *DeleteGlobalNotificationRequest) GetNotificationId() string { return "" } -// ListGlobalNotificationsRequest is the request for listing global notifications. -type ListGlobalNotificationsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // page_size is the size of the page to return. - 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 ListGlobalNotifications request, if any. - PageToken string `protobuf:"bytes,2,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` -} - -func (x *ListGlobalNotificationsRequest) Reset() { - *x = ListGlobalNotificationsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ListGlobalNotificationsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListGlobalNotificationsRequest) ProtoMessage() {} - -func (x *ListGlobalNotificationsRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[6] - 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 ListGlobalNotificationsRequest.ProtoReflect.Descriptor instead. -func (*ListGlobalNotificationsRequest) Descriptor() ([]byte, []int) { - return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{6} -} - -func (x *ListGlobalNotificationsRequest) GetPageSize() int32 { - if x != nil { - return x.PageSize - } - return 0 -} - -func (x *ListGlobalNotificationsRequest) GetPageToken() string { - if x != nil { - return x.PageToken - } - return "" -} - -// ListGlobalNotificationsResponse is the response from listing global notifications. -type ListGlobalNotificationsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // global_notifications is the global notification items returned. - GlobalNotifications []*GlobalNotification `protobuf:"bytes,1,rep,name=global_notifications,json=globalNotifications,proto3" json:"global_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"` -} - -func (x *ListGlobalNotificationsResponse) Reset() { - *x = ListGlobalNotificationsResponse{} - 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 *ListGlobalNotificationsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListGlobalNotificationsResponse) ProtoMessage() {} - -func (x *ListGlobalNotificationsResponse) 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 ListGlobalNotificationsResponse.ProtoReflect.Descriptor instead. -func (*ListGlobalNotificationsResponse) Descriptor() ([]byte, []int) { - return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{7} -} - -func (x *ListGlobalNotificationsResponse) GetGlobalNotifications() []*GlobalNotification { - if x != nil { - return x.GlobalNotifications - } - return nil -} - -func (x *ListGlobalNotificationsResponse) GetNextPageToken() string { - if x != nil { - return x.NextPageToken - } - return "" -} - -// CreateUserNotificationStateRequest is the request for creating a user notification state. -type CreateUserNotificationStateRequest struct { +// UpsertUserNotificationStateRequest is the request for creating or updating a user notification state. +type UpsertUserNotificationStateRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -505,23 +389,23 @@ type CreateUserNotificationStateRequest struct { UserNotificationState *UserNotificationState `protobuf:"bytes,2,opt,name=user_notification_state,json=userNotificationState,proto3" json:"user_notification_state,omitempty"` } -func (x *CreateUserNotificationStateRequest) Reset() { - *x = CreateUserNotificationStateRequest{} +func (x *UpsertUserNotificationStateRequest) Reset() { + *x = UpsertUserNotificationStateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[8] + mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *CreateUserNotificationStateRequest) String() string { +func (x *UpsertUserNotificationStateRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CreateUserNotificationStateRequest) ProtoMessage() {} +func (*UpsertUserNotificationStateRequest) ProtoMessage() {} -func (x *CreateUserNotificationStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[8] +func (x *UpsertUserNotificationStateRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -532,19 +416,19 @@ func (x *CreateUserNotificationStateRequest) ProtoReflect() protoreflect.Message return mi.MessageOf(x) } -// Deprecated: Use CreateUserNotificationStateRequest.ProtoReflect.Descriptor instead. -func (*CreateUserNotificationStateRequest) Descriptor() ([]byte, []int) { - return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{8} +// Deprecated: Use UpsertUserNotificationStateRequest.ProtoReflect.Descriptor instead. +func (*UpsertUserNotificationStateRequest) Descriptor() ([]byte, []int) { + return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{6} } -func (x *CreateUserNotificationStateRequest) GetUsername() string { +func (x *UpsertUserNotificationStateRequest) GetUsername() string { if x != nil { return x.Username } return "" } -func (x *CreateUserNotificationStateRequest) GetUserNotificationState() *UserNotificationState { +func (x *UpsertUserNotificationStateRequest) GetUserNotificationState() *UserNotificationState { if x != nil { return x.UserNotificationState } @@ -566,7 +450,7 @@ type UpdateUserNotificationStateRequest struct { func (x *UpdateUserNotificationStateRequest) Reset() { *x = UpdateUserNotificationStateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[9] + mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -579,7 +463,7 @@ func (x *UpdateUserNotificationStateRequest) String() string { func (*UpdateUserNotificationStateRequest) ProtoMessage() {} func (x *UpdateUserNotificationStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[9] + 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 { @@ -592,7 +476,7 @@ func (x *UpdateUserNotificationStateRequest) ProtoReflect() protoreflect.Message // Deprecated: Use UpdateUserNotificationStateRequest.ProtoReflect.Descriptor instead. func (*UpdateUserNotificationStateRequest) Descriptor() ([]byte, []int) { - return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{9} + return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{7} } func (x *UpdateUserNotificationStateRequest) GetUsername() string { @@ -624,7 +508,7 @@ type DeleteUserNotificationStateRequest struct { func (x *DeleteUserNotificationStateRequest) Reset() { *x = DeleteUserNotificationStateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[10] + mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -637,7 +521,7 @@ func (x *DeleteUserNotificationStateRequest) String() string { func (*DeleteUserNotificationStateRequest) ProtoMessage() {} func (x *DeleteUserNotificationStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[10] + 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 { @@ -650,7 +534,7 @@ func (x *DeleteUserNotificationStateRequest) ProtoReflect() protoreflect.Message // Deprecated: Use DeleteUserNotificationStateRequest.ProtoReflect.Descriptor instead. func (*DeleteUserNotificationStateRequest) Descriptor() ([]byte, []int) { - return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{10} + return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{8} } func (x *DeleteUserNotificationStateRequest) GetUsername() string { @@ -680,7 +564,7 @@ type ListUserNotificationStatesRequest struct { func (x *ListUserNotificationStatesRequest) Reset() { *x = ListUserNotificationStatesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[11] + mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -693,7 +577,7 @@ func (x *ListUserNotificationStatesRequest) String() string { func (*ListUserNotificationStatesRequest) ProtoMessage() {} func (x *ListUserNotificationStatesRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[11] + 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 { @@ -706,7 +590,7 @@ func (x *ListUserNotificationStatesRequest) ProtoReflect() protoreflect.Message // Deprecated: Use ListUserNotificationStatesRequest.ProtoReflect.Descriptor instead. func (*ListUserNotificationStatesRequest) Descriptor() ([]byte, []int) { - return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{11} + return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{9} } func (x *ListUserNotificationStatesRequest) GetUsername() string { @@ -731,7 +615,7 @@ type ListUserNotificationStatesResponse struct { func (x *ListUserNotificationStatesResponse) Reset() { *x = ListUserNotificationStatesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[12] + mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -744,7 +628,7 @@ func (x *ListUserNotificationStatesResponse) String() string { func (*ListUserNotificationStatesResponse) ProtoMessage() {} func (x *ListUserNotificationStatesResponse) ProtoReflect() protoreflect.Message { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[12] + 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 { @@ -757,7 +641,7 @@ func (x *ListUserNotificationStatesResponse) ProtoReflect() protoreflect.Message // Deprecated: Use ListUserNotificationStatesResponse.ProtoReflect.Descriptor instead. func (*ListUserNotificationStatesResponse) Descriptor() ([]byte, []int) { - return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{12} + return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{10} } func (x *ListUserNotificationStatesResponse) GetNotificationStates() []*UserNotificationState { @@ -787,7 +671,7 @@ type GetUserLastSeenNotificationRequest struct { func (x *GetUserLastSeenNotificationRequest) Reset() { *x = GetUserLastSeenNotificationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[13] + mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -800,7 +684,7 @@ func (x *GetUserLastSeenNotificationRequest) String() string { func (*GetUserLastSeenNotificationRequest) ProtoMessage() {} func (x *GetUserLastSeenNotificationRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[13] + 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 { @@ -813,7 +697,7 @@ func (x *GetUserLastSeenNotificationRequest) ProtoReflect() protoreflect.Message // Deprecated: Use GetUserLastSeenNotificationRequest.ProtoReflect.Descriptor instead. func (*GetUserLastSeenNotificationRequest) Descriptor() ([]byte, []int) { - return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{13} + return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{11} } func (x *GetUserLastSeenNotificationRequest) GetUsername() string { @@ -823,66 +707,8 @@ func (x *GetUserLastSeenNotificationRequest) GetUsername() string { return "" } -// CreateUserLastSeenNotification is the request for creating a user's last seen notification item. -type CreateUserLastSeenNotificationRequest 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 user last seen notification item to create. - UserLastSeenNotification *UserLastSeenNotification `protobuf:"bytes,2,opt,name=user_last_seen_notification,json=userLastSeenNotification,proto3" json:"user_last_seen_notification,omitempty"` -} - -func (x *CreateUserLastSeenNotificationRequest) Reset() { - *x = CreateUserLastSeenNotificationRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CreateUserLastSeenNotificationRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateUserLastSeenNotificationRequest) ProtoMessage() {} - -func (x *CreateUserLastSeenNotificationRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[14] - 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 CreateUserLastSeenNotificationRequest.ProtoReflect.Descriptor instead. -func (*CreateUserLastSeenNotificationRequest) Descriptor() ([]byte, []int) { - return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{14} -} - -func (x *CreateUserLastSeenNotificationRequest) GetUsername() string { - if x != nil { - return x.Username - } - return "" -} - -func (x *CreateUserLastSeenNotificationRequest) GetUserLastSeenNotification() *UserLastSeenNotification { - if x != nil { - return x.UserLastSeenNotification - } - return nil -} - -// UpdateUserLastSeenNotificationRequest is the request for updating a user's last seen notification. -type UpdateUserLastSeenNotificationRequest struct { +// UpsertUserLastSeenNotificationRequest is the request for creating or updating a user's last seen notification. +type UpsertUserLastSeenNotificationRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -893,23 +719,23 @@ type UpdateUserLastSeenNotificationRequest struct { UserLastSeenNotification *UserLastSeenNotification `protobuf:"bytes,2,opt,name=user_last_seen_notification,json=userLastSeenNotification,proto3" json:"user_last_seen_notification,omitempty"` } -func (x *UpdateUserLastSeenNotificationRequest) Reset() { - *x = UpdateUserLastSeenNotificationRequest{} +func (x *UpsertUserLastSeenNotificationRequest) Reset() { + *x = UpsertUserLastSeenNotificationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[15] + mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *UpdateUserLastSeenNotificationRequest) String() string { +func (x *UpsertUserLastSeenNotificationRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*UpdateUserLastSeenNotificationRequest) ProtoMessage() {} +func (*UpsertUserLastSeenNotificationRequest) ProtoMessage() {} -func (x *UpdateUserLastSeenNotificationRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[15] +func (x *UpsertUserLastSeenNotificationRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -920,19 +746,19 @@ func (x *UpdateUserLastSeenNotificationRequest) ProtoReflect() protoreflect.Mess return mi.MessageOf(x) } -// Deprecated: Use UpdateUserLastSeenNotificationRequest.ProtoReflect.Descriptor instead. -func (*UpdateUserLastSeenNotificationRequest) Descriptor() ([]byte, []int) { - return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{15} +// Deprecated: Use UpsertUserLastSeenNotificationRequest.ProtoReflect.Descriptor instead. +func (*UpsertUserLastSeenNotificationRequest) Descriptor() ([]byte, []int) { + return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{12} } -func (x *UpdateUserLastSeenNotificationRequest) GetUsername() string { +func (x *UpsertUserLastSeenNotificationRequest) GetUsername() string { if x != nil { return x.Username } return "" } -func (x *UpdateUserLastSeenNotificationRequest) GetUserLastSeenNotification() *UserLastSeenNotification { +func (x *UpsertUserLastSeenNotificationRequest) GetUserLastSeenNotification() *UserLastSeenNotification { if x != nil { return x.UserLastSeenNotification } @@ -952,7 +778,7 @@ type DeleteUserLastSeenNotificationRequest struct { func (x *DeleteUserLastSeenNotificationRequest) Reset() { *x = DeleteUserLastSeenNotificationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[16] + mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -965,7 +791,7 @@ func (x *DeleteUserLastSeenNotificationRequest) String() string { func (*DeleteUserLastSeenNotificationRequest) ProtoMessage() {} func (x *DeleteUserLastSeenNotificationRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_notifications_v1_notifications_service_proto_msgTypes[16] + 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 { @@ -978,7 +804,7 @@ func (x *DeleteUserLastSeenNotificationRequest) ProtoReflect() protoreflect.Mess // Deprecated: Use DeleteUserLastSeenNotificationRequest.ProtoReflect.Descriptor instead. func (*DeleteUserLastSeenNotificationRequest) Descriptor() ([]byte, []int) { - return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{16} + return file_teleport_notifications_v1_notifications_service_proto_rawDescGZIP(), []int{13} } func (x *DeleteUserLastSeenNotificationRequest) GetUsername() string { @@ -1046,226 +872,170 @@ var file_teleport_notifications_v1_notifications_service_proto_rawDesc = []byte{ 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, 0x5c, 0x0a, 0x1e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 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, 0xab, - 0x01, 0x0a, 0x1f, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x60, 0x0a, 0x14, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 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, - 0x13, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4e, 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, 0xaa, 0x01, 0x0a, - 0x22, 0x43, 0x72, 0x65, 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, 0xaa, 0x01, 0x0a, 0x22, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 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, + 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, 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, 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, + 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, 0xb7, 0x01, 0x0a, 0x25, 0x43, 0x72, 0x65, 0x61, 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, 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, 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, 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, 0xb7, 0x01, 0x0a, 0x25, 0x55, 0x70, 0x64, 0x61, 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, 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, + 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, 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, + 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, 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, - 0x8e, 0x0f, 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, + 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, 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, 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, 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, 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, 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, 0x90, 0x01, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x6c, - 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 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, 0x4c, 0x69, - 0x73, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, 0x2e, 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, 0x47, 0x6c, 0x6f, - 0x62, 0x61, 0x6c, 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, 0x43, 0x72, 0x65, - 0x61, 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, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x4e, + 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, + 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, + 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, - 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, 0x8e, 0x01, 0x0a, 0x1b, 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, 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, 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, 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, + 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, + 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, 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, + 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, 0x97, 0x01, 0x0a, 0x1e, 0x43, 0x72, 0x65, 0x61, 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, - 0x43, 0x72, 0x65, 0x61, 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, 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, 0x64, 0x61, 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, 0x55, 0x70, 0x64, 0x61, 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, - 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, + 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, } var ( @@ -1280,7 +1050,7 @@ 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, 17) +var file_teleport_notifications_v1_notifications_service_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_teleport_notifications_v1_notifications_service_proto_goTypes = []interface{}{ (*CreateUserNotificationRequest)(nil), // 0: teleport.notifications.v1.CreateUserNotificationRequest (*DeleteUserNotificationRequest)(nil), // 1: teleport.notifications.v1.DeleteUserNotificationRequest @@ -1288,66 +1058,55 @@ var file_teleport_notifications_v1_notifications_service_proto_goTypes = []inter (*ListUserNotificationsResponse)(nil), // 3: teleport.notifications.v1.ListUserNotificationsResponse (*CreateGlobalNotificationRequest)(nil), // 4: teleport.notifications.v1.CreateGlobalNotificationRequest (*DeleteGlobalNotificationRequest)(nil), // 5: teleport.notifications.v1.DeleteGlobalNotificationRequest - (*ListGlobalNotificationsRequest)(nil), // 6: teleport.notifications.v1.ListGlobalNotificationsRequest - (*ListGlobalNotificationsResponse)(nil), // 7: teleport.notifications.v1.ListGlobalNotificationsResponse - (*CreateUserNotificationStateRequest)(nil), // 8: teleport.notifications.v1.CreateUserNotificationStateRequest - (*UpdateUserNotificationStateRequest)(nil), // 9: teleport.notifications.v1.UpdateUserNotificationStateRequest - (*DeleteUserNotificationStateRequest)(nil), // 10: teleport.notifications.v1.DeleteUserNotificationStateRequest - (*ListUserNotificationStatesRequest)(nil), // 11: teleport.notifications.v1.ListUserNotificationStatesRequest - (*ListUserNotificationStatesResponse)(nil), // 12: teleport.notifications.v1.ListUserNotificationStatesResponse - (*GetUserLastSeenNotificationRequest)(nil), // 13: teleport.notifications.v1.GetUserLastSeenNotificationRequest - (*CreateUserLastSeenNotificationRequest)(nil), // 14: teleport.notifications.v1.CreateUserLastSeenNotificationRequest - (*UpdateUserLastSeenNotificationRequest)(nil), // 15: teleport.notifications.v1.UpdateUserLastSeenNotificationRequest - (*DeleteUserLastSeenNotificationRequest)(nil), // 16: teleport.notifications.v1.DeleteUserLastSeenNotificationRequest - (*Notification)(nil), // 17: teleport.notifications.v1.Notification - (*GlobalNotification)(nil), // 18: teleport.notifications.v1.GlobalNotification - (*UserNotificationState)(nil), // 19: teleport.notifications.v1.UserNotificationState - (*UserLastSeenNotification)(nil), // 20: teleport.notifications.v1.UserLastSeenNotification - (*emptypb.Empty)(nil), // 21: google.protobuf.Empty + (*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 } var file_teleport_notifications_v1_notifications_service_proto_depIdxs = []int32{ - 17, // 0: teleport.notifications.v1.CreateUserNotificationRequest.notification:type_name -> teleport.notifications.v1.Notification - 17, // 1: teleport.notifications.v1.ListUserNotificationsResponse.notifications:type_name -> teleport.notifications.v1.Notification - 18, // 2: teleport.notifications.v1.CreateGlobalNotificationRequest.global_notification:type_name -> teleport.notifications.v1.GlobalNotification - 18, // 3: teleport.notifications.v1.ListGlobalNotificationsResponse.global_notifications:type_name -> teleport.notifications.v1.GlobalNotification - 19, // 4: teleport.notifications.v1.CreateUserNotificationStateRequest.user_notification_state:type_name -> teleport.notifications.v1.UserNotificationState - 19, // 5: teleport.notifications.v1.UpdateUserNotificationStateRequest.user_notification_state:type_name -> teleport.notifications.v1.UserNotificationState - 19, // 6: teleport.notifications.v1.ListUserNotificationStatesResponse.notification_states:type_name -> teleport.notifications.v1.UserNotificationState - 20, // 7: teleport.notifications.v1.CreateUserLastSeenNotificationRequest.user_last_seen_notification:type_name -> teleport.notifications.v1.UserLastSeenNotification - 20, // 8: teleport.notifications.v1.UpdateUserLastSeenNotificationRequest.user_last_seen_notification:type_name -> teleport.notifications.v1.UserLastSeenNotification - 0, // 9: teleport.notifications.v1.NotificationService.CreateUserNotification:input_type -> teleport.notifications.v1.CreateUserNotificationRequest - 1, // 10: teleport.notifications.v1.NotificationService.DeleteUserNotification:input_type -> teleport.notifications.v1.DeleteUserNotificationRequest + 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 - 4, // 12: teleport.notifications.v1.NotificationService.CreateGlobalNotification:input_type -> teleport.notifications.v1.CreateGlobalNotificationRequest - 5, // 13: teleport.notifications.v1.NotificationService.DeleteGlobalNotification:input_type -> teleport.notifications.v1.DeleteGlobalNotificationRequest - 6, // 14: teleport.notifications.v1.NotificationService.ListGlobalNotifications:input_type -> teleport.notifications.v1.ListGlobalNotificationsRequest - 8, // 15: teleport.notifications.v1.NotificationService.CreateUserNotificationState:input_type -> teleport.notifications.v1.CreateUserNotificationStateRequest - 9, // 16: teleport.notifications.v1.NotificationService.UpdateUserNotificationState:input_type -> teleport.notifications.v1.UpdateUserNotificationStateRequest - 10, // 17: teleport.notifications.v1.NotificationService.DeleteUserNotificationState:input_type -> teleport.notifications.v1.DeleteUserNotificationStateRequest - 11, // 18: teleport.notifications.v1.NotificationService.ListUserNotificationStates:input_type -> teleport.notifications.v1.ListUserNotificationStatesRequest - 13, // 19: teleport.notifications.v1.NotificationService.GetUserLastSeenNotification:input_type -> teleport.notifications.v1.GetUserLastSeenNotificationRequest - 14, // 20: teleport.notifications.v1.NotificationService.CreateUserLastSeenNotification:input_type -> teleport.notifications.v1.CreateUserLastSeenNotificationRequest - 15, // 21: teleport.notifications.v1.NotificationService.UpdateUserLastSeenNotification:input_type -> teleport.notifications.v1.UpdateUserLastSeenNotificationRequest - 16, // 22: teleport.notifications.v1.NotificationService.DeleteUserLastSeenNotification:input_type -> teleport.notifications.v1.DeleteUserLastSeenNotificationRequest - 17, // 23: teleport.notifications.v1.NotificationService.CreateUserNotification:output_type -> teleport.notifications.v1.Notification - 21, // 24: teleport.notifications.v1.NotificationService.DeleteUserNotification:output_type -> google.protobuf.Empty - 3, // 25: teleport.notifications.v1.NotificationService.ListUserNotifications:output_type -> teleport.notifications.v1.ListUserNotificationsResponse - 18, // 26: teleport.notifications.v1.NotificationService.CreateGlobalNotification:output_type -> teleport.notifications.v1.GlobalNotification - 21, // 27: teleport.notifications.v1.NotificationService.DeleteGlobalNotification:output_type -> google.protobuf.Empty - 7, // 28: teleport.notifications.v1.NotificationService.ListGlobalNotifications:output_type -> teleport.notifications.v1.ListGlobalNotificationsResponse - 19, // 29: teleport.notifications.v1.NotificationService.CreateUserNotificationState:output_type -> teleport.notifications.v1.UserNotificationState - 19, // 30: teleport.notifications.v1.NotificationService.UpdateUserNotificationState:output_type -> teleport.notifications.v1.UserNotificationState - 21, // 31: teleport.notifications.v1.NotificationService.DeleteUserNotificationState:output_type -> google.protobuf.Empty - 12, // 32: teleport.notifications.v1.NotificationService.ListUserNotificationStates:output_type -> teleport.notifications.v1.ListUserNotificationStatesResponse - 20, // 33: teleport.notifications.v1.NotificationService.GetUserLastSeenNotification:output_type -> teleport.notifications.v1.UserLastSeenNotification - 20, // 34: teleport.notifications.v1.NotificationService.CreateUserLastSeenNotification:output_type -> teleport.notifications.v1.UserLastSeenNotification - 20, // 35: teleport.notifications.v1.NotificationService.UpdateUserLastSeenNotification:output_type -> teleport.notifications.v1.UserLastSeenNotification - 21, // 36: teleport.notifications.v1.NotificationService.DeleteUserLastSeenNotification:output_type -> google.protobuf.Empty - 23, // [23:37] is the sub-list for method output_type - 9, // [9:23] is the sub-list for method input_type - 9, // [9:9] is the sub-list for extension type_name - 9, // [9:9] is the sub-list for extension extendee - 0, // [0:9] is the sub-list for field type_name + 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 } func init() { file_teleport_notifications_v1_notifications_service_proto_init() } @@ -1430,7 +1189,7 @@ func file_teleport_notifications_v1_notifications_service_proto_init() { } } file_teleport_notifications_v1_notifications_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListGlobalNotificationsRequest); i { + switch v := v.(*UpsertUserNotificationStateRequest); i { case 0: return &v.state case 1: @@ -1442,7 +1201,7 @@ 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.(*ListGlobalNotificationsResponse); i { + switch v := v.(*UpdateUserNotificationStateRequest); i { case 0: return &v.state case 1: @@ -1454,7 +1213,7 @@ func file_teleport_notifications_v1_notifications_service_proto_init() { } } file_teleport_notifications_v1_notifications_service_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateUserNotificationStateRequest); i { + switch v := v.(*DeleteUserNotificationStateRequest); i { case 0: return &v.state case 1: @@ -1466,7 +1225,7 @@ func file_teleport_notifications_v1_notifications_service_proto_init() { } } file_teleport_notifications_v1_notifications_service_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateUserNotificationStateRequest); i { + switch v := v.(*ListUserNotificationStatesRequest); i { case 0: return &v.state case 1: @@ -1478,7 +1237,7 @@ func file_teleport_notifications_v1_notifications_service_proto_init() { } } file_teleport_notifications_v1_notifications_service_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteUserNotificationStateRequest); i { + switch v := v.(*ListUserNotificationStatesResponse); i { case 0: return &v.state case 1: @@ -1490,7 +1249,7 @@ func file_teleport_notifications_v1_notifications_service_proto_init() { } } file_teleport_notifications_v1_notifications_service_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListUserNotificationStatesRequest); i { + switch v := v.(*GetUserLastSeenNotificationRequest); i { case 0: return &v.state case 1: @@ -1502,7 +1261,7 @@ func file_teleport_notifications_v1_notifications_service_proto_init() { } } file_teleport_notifications_v1_notifications_service_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListUserNotificationStatesResponse); i { + switch v := v.(*UpsertUserLastSeenNotificationRequest); i { case 0: return &v.state case 1: @@ -1514,42 +1273,6 @@ func file_teleport_notifications_v1_notifications_service_proto_init() { } } file_teleport_notifications_v1_notifications_service_proto_msgTypes[13].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[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateUserLastSeenNotificationRequest); 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[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateUserLastSeenNotificationRequest); 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[16].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeleteUserLastSeenNotificationRequest); i { case 0: return &v.state @@ -1568,7 +1291,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: 17, + NumMessages: 14, 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 9a17303857955..4c84d1a6a27c5 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 @@ -1,6 +1,6 @@ // // Teleport -// Copyright (C) 2023 Gravitational, Inc. +// 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 @@ -39,17 +39,14 @@ const _ = grpc.SupportPackageIsVersion7 const ( NotificationService_CreateUserNotification_FullMethodName = "/teleport.notifications.v1.NotificationService/CreateUserNotification" NotificationService_DeleteUserNotification_FullMethodName = "/teleport.notifications.v1.NotificationService/DeleteUserNotification" - NotificationService_ListUserNotifications_FullMethodName = "/teleport.notifications.v1.NotificationService/ListUserNotifications" NotificationService_CreateGlobalNotification_FullMethodName = "/teleport.notifications.v1.NotificationService/CreateGlobalNotification" NotificationService_DeleteGlobalNotification_FullMethodName = "/teleport.notifications.v1.NotificationService/DeleteGlobalNotification" - NotificationService_ListGlobalNotifications_FullMethodName = "/teleport.notifications.v1.NotificationService/ListGlobalNotifications" - NotificationService_CreateUserNotificationState_FullMethodName = "/teleport.notifications.v1.NotificationService/CreateUserNotificationState" - NotificationService_UpdateUserNotificationState_FullMethodName = "/teleport.notifications.v1.NotificationService/UpdateUserNotificationState" + NotificationService_ListUserNotifications_FullMethodName = "/teleport.notifications.v1.NotificationService/ListUserNotifications" + 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_CreateUserLastSeenNotification_FullMethodName = "/teleport.notifications.v1.NotificationService/CreateUserLastSeenNotification" - NotificationService_UpdateUserLastSeenNotification_FullMethodName = "/teleport.notifications.v1.NotificationService/UpdateUserLastSeenNotification" + NotificationService_UpsertUserLastSeenNotification_FullMethodName = "/teleport.notifications.v1.NotificationService/UpsertUserLastSeenNotification" NotificationService_DeleteUserLastSeenNotification_FullMethodName = "/teleport.notifications.v1.NotificationService/DeleteUserLastSeenNotification" ) @@ -61,28 +58,22 @@ type NotificationServiceClient interface { CreateUserNotification(ctx context.Context, in *CreateUserNotificationRequest, opts ...grpc.CallOption) (*Notification, error) // DeleteUserNotification deletes a user-specific notification. DeleteUserNotification(ctx context.Context, in *DeleteUserNotificationRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) - // ListUserNotifications returns a page of user-specific notifications. - ListUserNotifications(ctx context.Context, in *ListUserNotificationsRequest, opts ...grpc.CallOption) (*ListUserNotificationsResponse, error) // CreateGlobalNotification creates a global notification. 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) - // ListGlobalNotifications returns a page of global notifications. - ListGlobalNotifications(ctx context.Context, in *ListGlobalNotificationsRequest, opts ...grpc.CallOption) (*ListGlobalNotificationsResponse, error) - // CreateUserNotificationState creates a user notification state which records whether a user has clicked on or dismissed a notification. - CreateUserNotificationState(ctx context.Context, in *CreateUserNotificationStateRequest, opts ...grpc.CallOption) (*UserNotificationState, error) - // UpdateUserNotificationState updates a user notification state to record whether a user has clicked on or dismissed a notification. - UpdateUserNotificationState(ctx context.Context, in *UpdateUserNotificationStateRequest, opts ...grpc.CallOption) (*UserNotificationState, 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) + // 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 user notification states. + // 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) - // CreateUserLastSeenNotification creates a user's last seen notification item. - CreateUserLastSeenNotification(ctx context.Context, in *CreateUserLastSeenNotificationRequest, opts ...grpc.CallOption) (*UserLastSeenNotification, error) - // UpdateUserLastSeenNotification updates a user's last seen notification item. - UpdateUserLastSeenNotification(ctx context.Context, in *UpdateUserLastSeenNotificationRequest, 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) } @@ -113,15 +104,6 @@ func (c *notificationServiceClient) DeleteUserNotification(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...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *notificationServiceClient) CreateGlobalNotification(ctx context.Context, in *CreateGlobalNotificationRequest, opts ...grpc.CallOption) (*GlobalNotification, error) { out := new(GlobalNotification) err := c.cc.Invoke(ctx, NotificationService_CreateGlobalNotification_FullMethodName, in, out, opts...) @@ -140,27 +122,18 @@ func (c *notificationServiceClient) DeleteGlobalNotification(ctx context.Context return out, nil } -func (c *notificationServiceClient) ListGlobalNotifications(ctx context.Context, in *ListGlobalNotificationsRequest, opts ...grpc.CallOption) (*ListGlobalNotificationsResponse, error) { - out := new(ListGlobalNotificationsResponse) - err := c.cc.Invoke(ctx, NotificationService_ListGlobalNotifications_FullMethodName, in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *notificationServiceClient) CreateUserNotificationState(ctx context.Context, in *CreateUserNotificationStateRequest, opts ...grpc.CallOption) (*UserNotificationState, error) { - out := new(UserNotificationState) - err := c.cc.Invoke(ctx, NotificationService_CreateUserNotificationState_FullMethodName, in, out, opts...) +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...) if err != nil { return nil, err } return out, nil } -func (c *notificationServiceClient) UpdateUserNotificationState(ctx context.Context, in *UpdateUserNotificationStateRequest, opts ...grpc.CallOption) (*UserNotificationState, error) { +func (c *notificationServiceClient) UpsertUserNotificationState(ctx context.Context, in *UpsertUserNotificationStateRequest, opts ...grpc.CallOption) (*UserNotificationState, error) { out := new(UserNotificationState) - err := c.cc.Invoke(ctx, NotificationService_UpdateUserNotificationState_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, NotificationService_UpsertUserNotificationState_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -194,18 +167,9 @@ func (c *notificationServiceClient) GetUserLastSeenNotification(ctx context.Cont return out, nil } -func (c *notificationServiceClient) CreateUserLastSeenNotification(ctx context.Context, in *CreateUserLastSeenNotificationRequest, opts ...grpc.CallOption) (*UserLastSeenNotification, error) { +func (c *notificationServiceClient) UpsertUserLastSeenNotification(ctx context.Context, in *UpsertUserLastSeenNotificationRequest, opts ...grpc.CallOption) (*UserLastSeenNotification, error) { out := new(UserLastSeenNotification) - err := c.cc.Invoke(ctx, NotificationService_CreateUserLastSeenNotification_FullMethodName, in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *notificationServiceClient) UpdateUserLastSeenNotification(ctx context.Context, in *UpdateUserLastSeenNotificationRequest, opts ...grpc.CallOption) (*UserLastSeenNotification, error) { - out := new(UserLastSeenNotification) - err := c.cc.Invoke(ctx, NotificationService_UpdateUserLastSeenNotification_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, NotificationService_UpsertUserLastSeenNotification_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -229,28 +193,22 @@ type NotificationServiceServer interface { CreateUserNotification(context.Context, *CreateUserNotificationRequest) (*Notification, error) // DeleteUserNotification deletes a user-specific notification. DeleteUserNotification(context.Context, *DeleteUserNotificationRequest) (*emptypb.Empty, error) - // ListUserNotifications returns a page of user-specific notifications. - ListUserNotifications(context.Context, *ListUserNotificationsRequest) (*ListUserNotificationsResponse, error) // CreateGlobalNotification creates a global notification. CreateGlobalNotification(context.Context, *CreateGlobalNotificationRequest) (*GlobalNotification, error) // DeleteGlobalNotification deletes a global notification. DeleteGlobalNotification(context.Context, *DeleteGlobalNotificationRequest) (*emptypb.Empty, error) - // ListGlobalNotifications returns a page of global notifications. - ListGlobalNotifications(context.Context, *ListGlobalNotificationsRequest) (*ListGlobalNotificationsResponse, error) - // CreateUserNotificationState creates a user notification state which records whether a user has clicked on or dismissed a notification. - CreateUserNotificationState(context.Context, *CreateUserNotificationStateRequest) (*UserNotificationState, error) - // UpdateUserNotificationState updates a user notification state to record whether a user has clicked on or dismissed a notification. - UpdateUserNotificationState(context.Context, *UpdateUserNotificationStateRequest) (*UserNotificationState, 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) + // 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 user notification states. + // 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) - // CreateUserLastSeenNotification creates a user's last seen notification item. - CreateUserLastSeenNotification(context.Context, *CreateUserLastSeenNotificationRequest) (*UserLastSeenNotification, error) - // UpdateUserLastSeenNotification updates a user's last seen notification item. - UpdateUserLastSeenNotification(context.Context, *UpdateUserLastSeenNotificationRequest) (*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() @@ -266,23 +224,17 @@ func (UnimplementedNotificationServiceServer) CreateUserNotification(context.Con func (UnimplementedNotificationServiceServer) DeleteUserNotification(context.Context, *DeleteUserNotificationRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteUserNotification not implemented") } -func (UnimplementedNotificationServiceServer) ListUserNotifications(context.Context, *ListUserNotificationsRequest) (*ListUserNotificationsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListUserNotifications not implemented") -} func (UnimplementedNotificationServiceServer) CreateGlobalNotification(context.Context, *CreateGlobalNotificationRequest) (*GlobalNotification, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateGlobalNotification not implemented") } func (UnimplementedNotificationServiceServer) DeleteGlobalNotification(context.Context, *DeleteGlobalNotificationRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteGlobalNotification not implemented") } -func (UnimplementedNotificationServiceServer) ListGlobalNotifications(context.Context, *ListGlobalNotificationsRequest) (*ListGlobalNotificationsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListGlobalNotifications not implemented") -} -func (UnimplementedNotificationServiceServer) CreateUserNotificationState(context.Context, *CreateUserNotificationStateRequest) (*UserNotificationState, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreateUserNotificationState not implemented") +func (UnimplementedNotificationServiceServer) ListUserNotifications(context.Context, *ListUserNotificationsRequest) (*ListUserNotificationsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListUserNotifications not implemented") } -func (UnimplementedNotificationServiceServer) UpdateUserNotificationState(context.Context, *UpdateUserNotificationStateRequest) (*UserNotificationState, error) { - return nil, status.Errorf(codes.Unimplemented, "method UpdateUserNotificationState 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") @@ -293,11 +245,8 @@ func (UnimplementedNotificationServiceServer) ListUserNotificationStates(context func (UnimplementedNotificationServiceServer) GetUserLastSeenNotification(context.Context, *GetUserLastSeenNotificationRequest) (*UserLastSeenNotification, error) { return nil, status.Errorf(codes.Unimplemented, "method GetUserLastSeenNotification not implemented") } -func (UnimplementedNotificationServiceServer) CreateUserLastSeenNotification(context.Context, *CreateUserLastSeenNotificationRequest) (*UserLastSeenNotification, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreateUserLastSeenNotification not implemented") -} -func (UnimplementedNotificationServiceServer) UpdateUserLastSeenNotification(context.Context, *UpdateUserLastSeenNotificationRequest) (*UserLastSeenNotification, error) { - return nil, status.Errorf(codes.Unimplemented, "method UpdateUserLastSeenNotification 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") @@ -351,24 +300,6 @@ func _NotificationService_DeleteUserNotification_Handler(srv interface{}, ctx co 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) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(NotificationServiceServer).ListUserNotifications(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: NotificationService_ListUserNotifications_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(NotificationServiceServer).ListUserNotifications(ctx, req.(*ListUserNotificationsRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _NotificationService_CreateGlobalNotification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CreateGlobalNotificationRequest) if err := dec(in); err != nil { @@ -405,56 +336,38 @@ func _NotificationService_DeleteGlobalNotification_Handler(srv interface{}, ctx return interceptor(ctx, in, info, handler) } -func _NotificationService_ListGlobalNotifications_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListGlobalNotificationsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(NotificationServiceServer).ListGlobalNotifications(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: NotificationService_ListGlobalNotifications_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(NotificationServiceServer).ListGlobalNotifications(ctx, req.(*ListGlobalNotificationsRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _NotificationService_CreateUserNotificationState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateUserNotificationStateRequest) +func _NotificationService_ListUserNotifications_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListUserNotificationsRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(NotificationServiceServer).CreateUserNotificationState(ctx, in) + return srv.(NotificationServiceServer).ListUserNotifications(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: NotificationService_CreateUserNotificationState_FullMethodName, + FullMethod: NotificationService_ListUserNotifications_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(NotificationServiceServer).CreateUserNotificationState(ctx, req.(*CreateUserNotificationStateRequest)) + return srv.(NotificationServiceServer).ListUserNotifications(ctx, req.(*ListUserNotificationsRequest)) } return interceptor(ctx, in, info, handler) } -func _NotificationService_UpdateUserNotificationState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateUserNotificationStateRequest) +func _NotificationService_UpsertUserNotificationState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpsertUserNotificationStateRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(NotificationServiceServer).UpdateUserNotificationState(ctx, in) + return srv.(NotificationServiceServer).UpsertUserNotificationState(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: NotificationService_UpdateUserNotificationState_FullMethodName, + FullMethod: NotificationService_UpsertUserNotificationState_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(NotificationServiceServer).UpdateUserNotificationState(ctx, req.(*UpdateUserNotificationStateRequest)) + return srv.(NotificationServiceServer).UpsertUserNotificationState(ctx, req.(*UpsertUserNotificationStateRequest)) } return interceptor(ctx, in, info, handler) } @@ -513,38 +426,20 @@ func _NotificationService_GetUserLastSeenNotification_Handler(srv interface{}, c return interceptor(ctx, in, info, handler) } -func _NotificationService_CreateUserLastSeenNotification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateUserLastSeenNotificationRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(NotificationServiceServer).CreateUserLastSeenNotification(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: NotificationService_CreateUserLastSeenNotification_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(NotificationServiceServer).CreateUserLastSeenNotification(ctx, req.(*CreateUserLastSeenNotificationRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _NotificationService_UpdateUserLastSeenNotification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateUserLastSeenNotificationRequest) +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 { return nil, err } if interceptor == nil { - return srv.(NotificationServiceServer).UpdateUserLastSeenNotification(ctx, in) + return srv.(NotificationServiceServer).UpsertUserLastSeenNotification(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: NotificationService_UpdateUserLastSeenNotification_FullMethodName, + FullMethod: NotificationService_UpsertUserLastSeenNotification_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(NotificationServiceServer).UpdateUserLastSeenNotification(ctx, req.(*UpdateUserLastSeenNotificationRequest)) + return srv.(NotificationServiceServer).UpsertUserLastSeenNotification(ctx, req.(*UpsertUserLastSeenNotificationRequest)) } return interceptor(ctx, in, info, handler) } @@ -582,10 +477,6 @@ var NotificationService_ServiceDesc = grpc.ServiceDesc{ MethodName: "DeleteUserNotification", Handler: _NotificationService_DeleteUserNotification_Handler, }, - { - MethodName: "ListUserNotifications", - Handler: _NotificationService_ListUserNotifications_Handler, - }, { MethodName: "CreateGlobalNotification", Handler: _NotificationService_CreateGlobalNotification_Handler, @@ -595,16 +486,12 @@ var NotificationService_ServiceDesc = grpc.ServiceDesc{ Handler: _NotificationService_DeleteGlobalNotification_Handler, }, { - MethodName: "ListGlobalNotifications", - Handler: _NotificationService_ListGlobalNotifications_Handler, - }, - { - MethodName: "CreateUserNotificationState", - Handler: _NotificationService_CreateUserNotificationState_Handler, + MethodName: "ListUserNotifications", + Handler: _NotificationService_ListUserNotifications_Handler, }, { - MethodName: "UpdateUserNotificationState", - Handler: _NotificationService_UpdateUserNotificationState_Handler, + MethodName: "UpsertUserNotificationState", + Handler: _NotificationService_UpsertUserNotificationState_Handler, }, { MethodName: "DeleteUserNotificationState", @@ -619,12 +506,8 @@ var NotificationService_ServiceDesc = grpc.ServiceDesc{ Handler: _NotificationService_GetUserLastSeenNotification_Handler, }, { - MethodName: "CreateUserLastSeenNotification", - Handler: _NotificationService_CreateUserLastSeenNotification_Handler, - }, - { - MethodName: "UpdateUserLastSeenNotification", - Handler: _NotificationService_UpdateUserLastSeenNotification_Handler, + MethodName: "UpsertUserLastSeenNotification", + Handler: _NotificationService_UpsertUserLastSeenNotification_Handler, }, { MethodName: "DeleteUserLastSeenNotification", diff --git a/api/proto/teleport/notifications/v1/notifications.proto b/api/proto/teleport/notifications/v1/notifications.proto index 314a97a5c6531..f75f4714751bd 100644 --- a/api/proto/teleport/notifications/v1/notifications.proto +++ b/api/proto/teleport/notifications/v1/notifications.proto @@ -1,6 +1,6 @@ /* * Teleport - * Copyright (C) 2023 Gravitational, Inc. + * 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 @@ -34,7 +34,7 @@ message Notification { string sub_kind = 2; // version is the resource version. string version = 3; - // metadata is the notification's metadata. This contains the notification's title, description, labels, and expiry. + // metadata is the notification's metadata. This contains the notification's labels, and expiry. All custom notification metadata should be stored in labels. teleport.header.v1.Metadata metadata = 4; // spec is the notification specification. NotificationSpec spec = 5; @@ -146,15 +146,17 @@ message UserLastSeenNotification { teleport.header.v1.Metadata metadata = 4; // UserLastSeenNotificationSpec is the user last seen notification item's specification. UserLastSeenNotificationSpec spec = 5; - // time is the timestamp of this user's last seen notification, it contains the timestamp of the notification which will be dynamically modified. - UserLastSeenNotificationTime time = 6; + reserved 6; + reserved "time"; + // status is the timestamp of this user's last seen notification, it contains the timestamp of the notification which will be dynamically modified. + UserLastSeenNotificationStatus status = 7; } // UserLastSeenNotificationSpec is a user last seen notification specification. message UserLastSeenNotificationSpec {} -// UserLastSeenNotificationTime is the timestamp of this user's last seen notification, it contains the timestamp of the notification which will be dynamically modified. -message UserLastSeenNotificationTime { +// UserLastSeenNotificationStatus is the timestamp of this user's last seen notification, it contains the timestamp of the notification which will be dynamically modified. +message UserLastSeenNotificationStatus { // last_seen_time is the timestamp of the last notification that the user has seen. google.protobuf.Timestamp last_seen_time = 1; } diff --git a/api/proto/teleport/notifications/v1/notifications_service.proto b/api/proto/teleport/notifications/v1/notifications_service.proto index 82ba37aeb9ff2..3d9ce708d8352 100644 --- a/api/proto/teleport/notifications/v1/notifications_service.proto +++ b/api/proto/teleport/notifications/v1/notifications_service.proto @@ -1,6 +1,6 @@ /* * Teleport - * Copyright (C) 2023 Gravitational, Inc. + * 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 @@ -31,31 +31,26 @@ service NotificationService { rpc CreateUserNotification(CreateUserNotificationRequest) returns (Notification); // DeleteUserNotification deletes a user-specific notification. rpc DeleteUserNotification(DeleteUserNotificationRequest) returns (google.protobuf.Empty); - // ListUserNotifications returns a page of user-specific notifications. - rpc ListUserNotifications(ListUserNotificationsRequest) returns (ListUserNotificationsResponse); // CreateGlobalNotification creates a global notification. rpc CreateGlobalNotification(CreateGlobalNotificationRequest) returns (GlobalNotification); // DeleteGlobalNotification deletes a global notification. rpc DeleteGlobalNotification(DeleteGlobalNotificationRequest) returns (google.protobuf.Empty); - // ListGlobalNotifications returns a page of global notifications. - rpc ListGlobalNotifications(ListGlobalNotificationsRequest) returns (ListGlobalNotificationsResponse); - // CreateUserNotificationState creates a user notification state which records whether a user has clicked on or dismissed a notification. - rpc CreateUserNotificationState(CreateUserNotificationStateRequest) returns (UserNotificationState); - // UpdateUserNotificationState updates a user notification state to record whether a user has clicked on or dismissed a notification. - rpc UpdateUserNotificationState(UpdateUserNotificationStateRequest) returns (UserNotificationState); + // 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); + + // 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 user notification states. + // 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); - // CreateUserLastSeenNotification creates a user's last seen notification item. - rpc CreateUserLastSeenNotification(CreateUserLastSeenNotificationRequest) returns (UserLastSeenNotification); - // UpdateUserLastSeenNotification updates a user's last seen notification item. - rpc UpdateUserLastSeenNotification(UpdateUserLastSeenNotificationRequest) 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); } @@ -76,7 +71,7 @@ message DeleteUserNotificationRequest { string notification_id = 2; } -// ListUserNotificationsRequest is the request for listing a user's user-specific notifications. +// 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; @@ -106,24 +101,8 @@ message DeleteGlobalNotificationRequest { string notification_id = 1; } -// ListGlobalNotificationsRequest is the request for listing global notifications. -message ListGlobalNotificationsRequest { - // page_size is the size of the page to return. - int32 page_size = 1; - // page_token is the next_page_token value returned from a previous ListGlobalNotifications request, if any. - string page_token = 2; -} - -// ListGlobalNotificationsResponse is the response from listing global notifications. -message ListGlobalNotificationsResponse { - // global_notifications is the global notification items returned. - repeated GlobalNotification global_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; -} - -// CreateUserNotificationStateRequest is the request for creating a user notification state. -message CreateUserNotificationStateRequest { +// UpsertUserNotificationStateRequest is the request for creating or updating a user notification state. +message UpsertUserNotificationStateRequest { // username is the username of the user. string username = 1; // user_notification_state is the user notification state to create. @@ -166,16 +145,8 @@ message GetUserLastSeenNotificationRequest { string username = 1; } -// CreateUserLastSeenNotification is the request for creating a user's last seen notification item. -message CreateUserLastSeenNotificationRequest { - // username is the username of the user. - string username = 1; - // user_notification_state is the user last seen notification item to create. - UserLastSeenNotification user_last_seen_notification = 2; -} - -// UpdateUserLastSeenNotificationRequest is the request for updating a user's last seen notification. -message UpdateUserLastSeenNotificationRequest { +// 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. diff --git a/api/types/constants.go b/api/types/constants.go index 5f0060753e3ef..f0a6a6c356087 100644 --- a/api/types/constants.go +++ b/api/types/constants.go @@ -503,6 +503,15 @@ const ( // KindSecurityReportCostLimiter const limiter KindSecurityReportCostLimiter = "security_report_cost_limiter" + // KindNotification is a notification resource. + KindNotification = "notification" + // KindGlobalNotification is a global notification resource. + KindGlobalNotification = "global_notification" + // KindUserLastSeenNotification is a resource which stores the timestamp of a user's last seen notification. + KindUserLastSeenNotification = "user_last_seen_notification" + // KindUserNotificationState is a resource which tracks whether a user has clicked on or dismissed a notification. + KindUserNotificationState = "user_notification_state" + // V7 is the seventh version of resources. V7 = "v7" diff --git a/lib/services/local/generic/generic_wrapper.go b/lib/services/local/generic/generic_wrapper.go index a0d1e3e1fa201..1acf174789866 100644 --- a/lib/services/local/generic/generic_wrapper.go +++ b/lib/services/local/generic/generic_wrapper.go @@ -18,6 +18,7 @@ package generic import ( "context" + "strings" "github.com/gravitational/trace" @@ -62,6 +63,24 @@ type ServiceWrapper[T types.ResourceMetadata] struct { service *Service[resourceMetadataAdapter[T]] } +// WithPrefix will return a service wrapper with the given parts appended to the backend prefix. +func (s ServiceWrapper[T]) WithPrefix(parts ...string) *ServiceWrapper[T] { + if len(parts) == 0 { + return &s + } + + return &ServiceWrapper[T]{ + service: &Service[resourceMetadataAdapter[T]]{ + backend: s.service.backend, + resourceKind: s.service.resourceKind, + pageLimit: s.service.pageLimit, + backendPrefix: strings.Join(append([]string{s.service.backendPrefix}, parts...), string(backend.Separator)), + marshalFunc: s.service.marshalFunc, + unmarshalFunc: s.service.unmarshalFunc, + }, + } +} + // UpsertResource upserts a resource. func (s ServiceWrapper[T]) UpsertResource(ctx context.Context, resource T) (T, error) { adapter, err := s.service.UpsertResource(ctx, newResourceMetadataAdapter(resource)) @@ -91,6 +110,12 @@ func (s ServiceWrapper[T]) DeleteResource(ctx context.Context, name string) erro return trace.Wrap(s.service.DeleteResource(ctx, name)) } +// DeleteAllResources removes all resources. +func (s ServiceWrapper[T]) DeleteAllResources(ctx context.Context) error { + startKey := backend.ExactKey(s.service.backendPrefix) + return trace.Wrap(s.service.backend.DeleteRange(ctx, startKey, backend.RangeEnd(startKey))) +} + // ListResources returns a paginated list of resources. func (s ServiceWrapper[T]) ListResources(ctx context.Context, pageSize int, pageToken string) ([]T, string, error) { adapters, nextToken, err := s.service.ListResources(ctx, pageSize, pageToken) diff --git a/lib/services/local/generic/generic_wrapper_test.go b/lib/services/local/generic/generic_wrapper_test.go index d77612857999f..3722db1e62784 100644 --- a/lib/services/local/generic/generic_wrapper_test.go +++ b/lib/services/local/generic/generic_wrapper_test.go @@ -224,3 +224,31 @@ func TestGenericWrapperCRUD(t *testing.T) { err = service.DeleteResource(ctx, "doesnotexist") require.True(t, trace.IsNotFound(err)) } + +// TestGenericWrapperWithPrefix tests the withPrefix method of the generic service wrapper. +func TestGenericWrapperWithPrefix(t *testing.T) { + ctx := context.Background() + + memBackend, err := memory.New(memory.Config{ + Context: ctx, + Clock: clockwork.NewFakeClock(), + }) + require.NoError(t, err) + + const initialBackendPrefix = "initial_prefix" + const additionalBackendPrefix = "additional_prefix" + + service, err := NewServiceWrapper[*testResource153](memBackend, + "generic resource", + initialBackendPrefix, + marshalResource153, + unmarshalResource153) + require.NoError(t, err) + + // Verify that the service's backend prefix matches the initial backend prefix. + require.Equal(t, initialBackendPrefix, service.service.backendPrefix) + + // Verify that withPrefix appends the the additional prefix. + serviceWithPrefix := service.WithPrefix(additionalBackendPrefix) + require.Equal(t, "initial_prefix/additional_prefix", serviceWithPrefix.service.backendPrefix) +} diff --git a/lib/services/local/notifications.go b/lib/services/local/notifications.go new file mode 100644 index 0000000000000..1fe85d1151c90 --- /dev/null +++ b/lib/services/local/notifications.go @@ -0,0 +1,309 @@ +/* + * 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 local + +import ( + "context" + "time" + + "github.com/google/uuid" + "github.com/gravitational/trace" + "github.com/jonboulle/clockwork" + "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/backend" + "github.com/gravitational/teleport/lib/services" + "github.com/gravitational/teleport/lib/services/local/generic" +) + +// NotificationsService manages notification resources in the backend. +type NotificationsService struct { + clock clockwork.Clock + userNotificationService *generic.ServiceWrapper[*notificationsv1.Notification] + globalNotificationService *generic.ServiceWrapper[*notificationsv1.GlobalNotification] + userNotificationStateService *generic.ServiceWrapper[*notificationsv1.UserNotificationState] + userLastSeenNotificationService *generic.ServiceWrapper[*notificationsv1.UserLastSeenNotification] +} + +// NewNotificationsService returns a new instance of the NotificationService. +func NewNotificationsService(backend backend.Backend, clock clockwork.Clock) (*NotificationsService, error) { + userNotificationService, err := generic.NewServiceWrapper[*notificationsv1.Notification](backend, types.KindNotification, notificationsUserSpecificPrefix, services.MarshalNotification, services.UnmarshalNotification) + if err != nil { + return nil, trace.Wrap(err) + } + + globalNotificationService, err := generic.NewServiceWrapper[*notificationsv1.GlobalNotification](backend, types.KindGlobalNotification, notificationsGlobalPrefix, services.MarshalGlobalNotification, services.UnmarshalGlobalNotification) + if err != nil { + return nil, trace.Wrap(err) + } + + userNotificationStateService, err := generic.NewServiceWrapper[*notificationsv1.UserNotificationState](backend, types.KindUserNotificationState, notificationsStatePrefix, services.MarshalUserNotificationState, services.UnmarshalUserNotificationState) + if err != nil { + return nil, trace.Wrap(err) + } + + userLastSeenNotificationService, err := generic.NewServiceWrapper[*notificationsv1.UserLastSeenNotification](backend, types.KindUserLastSeenNotification, notificationsUserLastSeenPrefix, services.MarshalUserLastSeenNotification, services.UnmarshalUserLastSeenNotification) + if err != nil { + return nil, trace.Wrap(err) + } + + return &NotificationsService{ + clock: clock, + userNotificationService: userNotificationService, + globalNotificationService: globalNotificationService, + userNotificationStateService: userNotificationStateService, + userLastSeenNotificationService: userLastSeenNotificationService, + }, nil +} + +// ListNotificationsForUser returns a paginated list of notifications which match a user, including both user-specific and global ones. +func (s *NotificationsService) ListNotificationsForUser(ctx context.Context) ([]*notificationsv1.Notification, string, error) { + // TODO: rudream - implement listing notifications for a user with filtering/matching + return []*notificationsv1.Notification{}, "", nil +} + +// CreateUserNotification creates a user-specific notification. +func (s *NotificationsService) CreateUserNotification(ctx context.Context, username string, notification *notificationsv1.Notification) (*notificationsv1.Notification, error) { + if err := services.ValidateNotification(notification); err != nil { + return nil, trace.Wrap(err) + } + + notification.Kind = types.KindNotification + notification.Version = types.V1 + + // Generate uuidv7 ID. + uuid, err := uuid.NewV7() + 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 + + if err := CheckAndSetExpiry(notification, s.clock); err != nil { + return nil, trace.Wrap(err) + } + + notification.Spec.Created = timestamppb.New(s.clock.Now()) + + // Append username prefix. + serviceWithPrefix := s.userNotificationService.WithPrefix(username) + + created, err := serviceWithPrefix.CreateResource(ctx, notification) + return created, trace.Wrap(err) +} + +// DeleteUserNotification deletes a user-specific notification. +func (s *NotificationsService) DeleteUserNotification(ctx context.Context, username string, notificationId string) error { + // Append username prefix. + serviceWithPrefix := s.userNotificationService.WithPrefix(username) + + // Delete the notification + if err := serviceWithPrefix.DeleteResource(ctx, notificationId); err != nil { + return trace.Wrap(err) + } + + // Also delete the user notification state for this notification. + notificationStateServiceWithPrefix := s.userNotificationStateService.WithPrefix(username) + + if err := notificationStateServiceWithPrefix.DeleteResource(ctx, notificationId); err != nil { + // If the error is due to the user notification state not being found, then ignore it because + // it is possible that it doesn't exist (if the user never clicked on or dismissed the notification). + if trace.IsNotFound(err) { + return nil + } + return trace.Wrap(err) + } + return nil +} + +// DeleteAllUserNotificationsForUser deletes all of a user's user-specific notifications. +func (s *NotificationsService) DeleteAllUserNotificationsForUser(ctx context.Context, username string) error { + // Append username prefix. + serviceWithPrefix := s.userNotificationService.WithPrefix(username) + + err := serviceWithPrefix.DeleteAllResources(ctx) + return trace.Wrap(err) +} + +// CreateGlobalNotification creates a global notification. +func (s *NotificationsService) CreateGlobalNotification(ctx context.Context, globalNotification *notificationsv1.GlobalNotification) (*notificationsv1.GlobalNotification, error) { + if err := services.ValidateGlobalNotification(globalNotification); err != nil { + return nil, trace.Wrap(err) + } + + // Check to ensure that the metadata for the globalNotification isn't configured, this shouldn't be used and if it is configured, the caller likely meant to + // configure the notification's metadata, which is in spec.notification.metadata. + // We do this check here instead of in `ValidateGlobalNotification` because we only want to do this check on creation. + if globalNotification.Metadata != nil { + return nil, trace.BadParameter("metadata should be nil, metadata for a notification should be in spec.notification.metadata") + } + + globalNotification.Kind = types.KindGlobalNotification + globalNotification.Version = types.V1 + + // Generate uuidv7 ID. + uuid, err := uuid.NewV7() + 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} + + if err := CheckAndSetExpiry(globalNotification.Spec.Notification, s.clock); err != nil { + return nil, trace.Wrap(err) + } + + globalNotification.Spec.Notification.Spec.Created = timestamppb.New(s.clock.Now()) + + created, err := s.globalNotificationService.CreateResource(ctx, globalNotification) + return created, trace.Wrap(err) +} + +// DeleteGlobalNotification deletes a global notification. +func (s *NotificationsService) DeleteGlobalNotification(ctx context.Context, notificationId string) error { + err := s.globalNotificationService.DeleteResource(ctx, notificationId) + return trace.Wrap(err) +} + +// UpsertUserNotificationState creates or updates a user notification state which records whether the user has clicked on or dismissed a notification. +func (s *NotificationsService) UpsertUserNotificationState(ctx context.Context, username string, state *notificationsv1.UserNotificationState) (*notificationsv1.UserNotificationState, error) { + if err := services.ValidateUserNotificationState(state); err != nil { + return nil, trace.Wrap(err) + } + + // 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) + } + + state.Kind = types.KindUserNotificationState + state.Version = types.V1 + + if state.Metadata == nil { + state.Metadata = &headerv1.Metadata{} + } + + // We set this to the notification UUID because the service adapter uses `getName()` to determine the backend key to use when storing the object. + state.Metadata.Name = state.Spec.NotificationId + + // Append username prefix. + serviceWithPrefix := s.userNotificationStateService.WithPrefix(username) + + upserted, err := serviceWithPrefix.UpsertResource(ctx, state) + return upserted, trace.Wrap(err) +} + +// DeleteUserNotificationState deletes a user notification state object. +func (s *NotificationsService) DeleteUserNotificationState(ctx context.Context, username string, notificationId string) error { + // Append username prefix. + serviceWithPrefix := s.userNotificationStateService.WithPrefix(username) + + err := serviceWithPrefix.DeleteResource(ctx, notificationId) + return trace.Wrap(err) +} + +// DeleteAllUserNotificationStatesForUser deletes all of a user's notification states. +func (s *NotificationsService) DeleteAllUserNotificationStatesForUser(ctx context.Context, username string) error { + // Append username prefix. + serviceWithPrefix := s.userNotificationStateService.WithPrefix(username) + + err := serviceWithPrefix.DeleteAllResources(ctx) + return trace.Wrap(err) +} + +// ListUserNotificationStates returns a page of a user's notification states. +func (s *NotificationsService) ListUserNotificationStates(ctx context.Context, username string, pageSize int, nextToken string) ([]*notificationsv1.UserNotificationState, string, error) { + // Append username prefix. + serviceWithPrefix := s.userNotificationStateService.WithPrefix(username) + + states, nextToken, err := serviceWithPrefix.ListResources(ctx, pageSize, nextToken) + return states, nextToken, trace.Wrap(err) +} + +// UpsertUserLastSeenNotification creates or updates a user's last seen notification item. +func (s *NotificationsService) UpsertUserLastSeenNotification(ctx context.Context, username string, ulsn *notificationsv1.UserLastSeenNotification) (*notificationsv1.UserLastSeenNotification, error) { + if err := services.ValidateUserLastSeenNotification(ulsn); err != nil { + return nil, trace.Wrap(err) + } + + ulsn.Kind = types.KindUserLastSeenNotification + ulsn.Version = types.V1 + + if ulsn.Metadata == nil { + ulsn.Metadata = &headerv1.Metadata{} + } + // We set this to the username because the service adapter uses `getName()` to determine the backend key to use when storing the object. + ulsn.Metadata.Name = username + + upserted, err := s.userLastSeenNotificationService.UpsertResource(ctx, ulsn) + return upserted, trace.Wrap(err) +} + +// GetUserLastSeenNotification returns a user's last seen notification item. +func (s *NotificationsService) GetUserLastSeenNotification(ctx context.Context, username string) (*notificationsv1.UserLastSeenNotification, error) { + + ulsn, err := s.userLastSeenNotificationService.GetResource(ctx, username) + return ulsn, trace.Wrap(err) +} + +// DeleteUserLastSeenNotification deletes a user's last seen notification item. +func (s *NotificationsService) DeleteUserLastSeenNotification(ctx context.Context, username string) error { + err := s.userLastSeenNotificationService.DeleteResource(ctx, username) + return trace.Wrap(err) +} + +// CheckAndSetExpiry checks and sets the default expiry for a notification. +func CheckAndSetExpiry(notification *notificationsv1.Notification, clock clockwork.Clock) error { + // If the expiry hasn't been provided, set the default to 30 days from now. + if notification.Metadata.Expires == nil { + now := clock.Now() + futureTime := now.Add(defaultExpiry) + notification.Metadata.Expires = timestamppb.New(futureTime) + return nil + } + + // If the expiry has already been provided, ensure that it is not more than 90 days from now. + // This is to prevent misuse as we don't want notifications existing for too long and accumulating in the backend. + now := clock.Now() + timeOfMaxExpiry := now.Add(maxExpiry) + + if (*notification.Metadata.Expires).AsTime().After(timeOfMaxExpiry) { + return trace.BadParameter("notification expiry cannot be more than %d days from its creation", int(maxExpiry.Hours()/24)) + } + + return nil +} + +const ( + notificationsGlobalPrefix = "notifications/global" // notifications/global/ + notificationsUserSpecificPrefix = "notifications/user" // notifications/user// + notificationsStatePrefix = "notifications/states" // notifications/states// + notificationsUserLastSeenPrefix = "notifications/last_seen" // notifications/last_seen/ + + defaultExpiry = 30 * 24 * time.Hour // The default expiry for a notification, 30 days. + maxExpiry = 90 * 24 * time.Hour // The maximum expiry for a notification, 90 days. +) diff --git a/lib/services/local/notifications_test.go b/lib/services/local/notifications_test.go new file mode 100644 index 0000000000000..335a60cfd0f24 --- /dev/null +++ b/lib/services/local/notifications_test.go @@ -0,0 +1,404 @@ +/* + * 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 local + +import ( + "context" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/gravitational/trace" + "github.com/jonboulle/clockwork" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/testing/protocmp" + "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/lib/backend" + "github.com/gravitational/teleport/lib/backend/memory" +) + +// TestUserNotificationCRUD tests backend operations for user-specific notification resources. +func TestUserNotificationCRUD(t *testing.T) { + ctx := context.Background() + clock := clockwork.NewFakeClock() + + mem, err := memory.New(memory.Config{ + Context: ctx, + Clock: clock, + }) + require.NoError(t, err) + + service, err := NewNotificationsService(backend.NewSanitizer(mem), clock) + require.NoError(t, err) + + testUsername := "test-username" + + // Create a couple notifications. + userNotification1 := newUserNotification(t, "test-notification-1") + userNotification2 := newUserNotification(t, "test-notification-2") + + // Create notifications. + notification, err := service.CreateUserNotification(ctx, testUsername, userNotification1) + require.NoError(t, err) + require.Empty(t, cmp.Diff(userNotification1, notification, protocmp.Transform())) + notification1Id := notification.Spec.Id + notification, err = service.CreateUserNotification(ctx, testUsername, userNotification2) + require.NoError(t, err) + require.Empty(t, cmp.Diff(userNotification2, notification, protocmp.Transform())) + notification2Id := notification.Spec.Id + + // Test deleting a notification. + err = service.DeleteUserNotification(ctx, testUsername, notification1Id) + require.NoError(t, err) + // Since we don't have any Get or List method for user-specific notifications specifically, we will assert that it was deleted + // by attempting to delete it again and expecting a "not found" error. + err = service.DeleteUserNotification(ctx, testUsername, notification1Id) + require.True(t, trace.IsNotFound(err), "got error %T, expected a not found error due to notification test-notification-1 not existing", err) + + // Test deleting a notification that doesn't exist. + err = service.DeleteUserNotification(ctx, testUsername, "invalid-id") + require.True(t, trace.IsNotFound(err), "got error %T, expected a not found error due to notification invalid-id not existing", err) + + // Test deleting all of a user's user-specific notifications. + // Upsert userNotification1 again. + // We reset it to the mock first because the previous CreateUserNotification will have mutated it and populated the `Created` field which should be empty. + userNotification1 = newUserNotification(t, "test-notification-1") + _, err = service.CreateUserNotification(ctx, testUsername, userNotification1) + require.NoError(t, err) + notification1Id = notification.Spec.Id + err = service.DeleteAllUserNotificationsForUser(ctx, testUsername) + require.NoError(t, err) + // Verify that the notifications don't exist anymore by attempting to delete them. + err = service.DeleteUserNotification(ctx, testUsername, notification1Id) + require.True(t, trace.IsNotFound(err), "got error %T, expected a not found error due to notification test-notification-1 not existing", err) + err = service.DeleteUserNotification(ctx, testUsername, notification2Id) + require.True(t, trace.IsNotFound(err), "got error %T, expected a not found error due to notification test-notification-2 not existing", err) + +} + +// TestGlobalNotificationCRUD tests backend operations for global notification resources. +func TestGlobalNotificationCRUD(t *testing.T) { + ctx := context.Background() + clock := clockwork.NewFakeClock() + + mem, err := memory.New(memory.Config{ + Context: ctx, + Clock: clock, + }) + require.NoError(t, err) + + service, err := NewNotificationsService(backend.NewSanitizer(mem), clock) + require.NoError(t, err) + + // Create a couple notifications. + globalNotification1 := newGlobalNotification(t, "test-notification-1") + globalNotification2 := newGlobalNotification(t, "test-notification-2") + globalNotificationNoMatcher := ¬ificationsv1.GlobalNotification{ + Spec: ¬ificationsv1.GlobalNotificationSpec{ + Notification: ¬ificationsv1.Notification{ + SubKind: "test-subkind", + Spec: ¬ificationsv1.NotificationSpec{}, + Metadata: &headerv1.Metadata{ + Description: "Test Description", + }, + }, + }, + } + globalNotificationLateExpiry := ¬ificationsv1.GlobalNotification{ + Spec: ¬ificationsv1.GlobalNotificationSpec{ + Matcher: ¬ificationsv1.GlobalNotificationSpec_All{ + All: true, + }, + Notification: ¬ificationsv1.Notification{ + SubKind: "test-subkind", + Spec: ¬ificationsv1.NotificationSpec{}, + Metadata: &headerv1.Metadata{ + Description: "Test Description", + Labels: map[string]string{"description": "notification-late-expiry"}, + // Set the expiry to 91 days from now, which is past the 90 day expiry limit. + Expires: timestamppb.New(clock.Now().AddDate(0, 0, 91)), + }, + }, + }, + } + + // Create notifications. + 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 + notification, err = service.CreateGlobalNotification(ctx, globalNotification2) + require.NoError(t, err) + require.Empty(t, cmp.Diff(globalNotification2, notification, protocmp.Transform())) + // Expect error due to having no matcher. + _, err = service.CreateGlobalNotification(ctx, globalNotificationNoMatcher) + require.True(t, trace.IsBadParameter(err), "got error %T, expected a bad parameter error due to notification-no-matcher having no matcher", err) + // Expect error due to expiry date being more than 90 days from now. + _, err = service.CreateGlobalNotification(ctx, globalNotificationLateExpiry) + require.True(t, trace.IsBadParameter(err), "got error %T, expected a bad parameter error due to notification-late-expiry having an expiry date more than 90 days later", err) + + // Test deleting a notification. + err = service.DeleteGlobalNotification(ctx, globalNotification1Id) + require.NoError(t, err) + // Test deleting a notification that doesn't exist. + err = service.DeleteGlobalNotification(ctx, "invalid-id") + require.True(t, trace.IsNotFound(err), "got error %T, expected a not found error due to notification invalid-id not existing", err) +} + +// TestUserNotificationStateCRUD tests backend operations for user-specific notification resources. +func TestUserNotificationStateCRUD(t *testing.T) { + ctx := context.Background() + clock := clockwork.NewFakeClock() + + mem, err := memory.New(memory.Config{ + Context: ctx, + Clock: clock, + }) + require.NoError(t, err) + + service, err := NewNotificationsService(backend.NewSanitizer(mem), clock) + require.NoError(t, err) + + testUsername := "test-username" + + // Create a and upsert the notifications that these states will be for. + userNotification1 := newUserNotification(t, "test-notification-1") + userNotification2 := newUserNotification(t, "test-notification-2") + notification, err := service.CreateUserNotification(ctx, testUsername, userNotification1) + require.NoError(t, err) + notification1Id := notification.Spec.Id + notification, err = service.CreateUserNotification(ctx, testUsername, userNotification2) + require.NoError(t, err) + notification2Id := notification.Spec.Id + + userNotificationState1 := ¬ificationsv1.UserNotificationState{ + Spec: ¬ificationsv1.UserNotificationStateSpec{ + NotificationId: notification1Id, + }, + Status: ¬ificationsv1.UserNotificationStateStatus{ + NotificationState: notificationsv1.NotificationState_NOTIFICATION_STATE_CLICKED, + }, + } + + // Duplicate of the above but with the state set to dismissed instead of clicked. + userNotificationState1Dismissed := ¬ificationsv1.UserNotificationState{ + Spec: ¬ificationsv1.UserNotificationStateSpec{ + NotificationId: notification1Id, + }, + Status: ¬ificationsv1.UserNotificationStateStatus{ + NotificationState: notificationsv1.NotificationState_NOTIFICATION_STATE_DISMISSED, + }, + } + + userNotificationState2 := ¬ificationsv1.UserNotificationState{ + Spec: ¬ificationsv1.UserNotificationStateSpec{ + NotificationId: notification2Id, + }, + Status: ¬ificationsv1.UserNotificationStateStatus{ + NotificationState: notificationsv1.NotificationState_NOTIFICATION_STATE_CLICKED, + }, + } + + // Initially we expect no user notification states. + out, nextToken, err := service.ListUserNotificationStates(ctx, testUsername, 0, "") + require.NoError(t, err) + require.Empty(t, nextToken) + require.Empty(t, out) + + // Upsert notification states. + notificationState, err := service.UpsertUserNotificationState(ctx, testUsername, userNotificationState1) + require.NoError(t, err) + require.Empty(t, cmp.Diff(userNotificationState1, notificationState, protocmp.Transform())) + notificationState, err = service.UpsertUserNotificationState(ctx, testUsername, userNotificationState2) + require.NoError(t, err) + require.Empty(t, cmp.Diff(userNotificationState2, notificationState, protocmp.Transform())) + + // Fetch a paginated list of the user's notification states. + paginatedOut := make([]*notificationsv1.UserNotificationState, 0, 2) + for { + out, nextToken, err = service.ListUserNotificationStates(ctx, testUsername, 1, nextToken) + require.NoError(t, err) + + paginatedOut = append(paginatedOut, out...) + if nextToken == "" { + break + } + } + + cmpOpts := []cmp.Option{ + protocmp.IgnoreFields(&headerv1.Metadata{}, "id", "revision"), + protocmp.Transform(), + } + + require.Len(t, paginatedOut, 2) + // Verify that notification states returned are correct. + require.Empty(t, cmp.Diff([]*notificationsv1.UserNotificationState{userNotificationState1, userNotificationState2}, paginatedOut, cmpOpts...)) + + // Upsert a dismissed state with for the same notification id as userNotificationState1. + notificationState, err = service.UpsertUserNotificationState(ctx, testUsername, userNotificationState1Dismissed) + require.NoError(t, err) + require.Empty(t, cmp.Diff(userNotificationState1Dismissed, notificationState, cmpOpts...)) + + // Fetch the list again. + paginatedOut = make([]*notificationsv1.UserNotificationState, 0, 2) + for { + out, nextToken, err = service.ListUserNotificationStates(ctx, testUsername, 1, nextToken) + require.NoError(t, err) + + paginatedOut = append(paginatedOut, out...) + if nextToken == "" { + break + } + } + + require.Len(t, paginatedOut, 2) + // Verify that notification id's and states are correct, userNotificationState1 should now have the dismissed state. + require.Equal(t, userNotificationState1.Spec.NotificationId, paginatedOut[0].Spec.NotificationId) + require.Equal(t, notificationsv1.NotificationState_NOTIFICATION_STATE_DISMISSED, paginatedOut[0].Status.NotificationState) + require.Equal(t, userNotificationState2.Spec.NotificationId, paginatedOut[1].Spec.NotificationId) + require.Equal(t, notificationsv1.NotificationState_NOTIFICATION_STATE_CLICKED, paginatedOut[1].Status.NotificationState) + + // Test deleting a notification state. + err = service.DeleteUserNotificationState(ctx, testUsername, notification1Id) + require.NoError(t, err) + // Test deleting a notification state that doesn't exist. + err = service.DeleteUserNotificationState(ctx, testUsername, "invalid-id") + require.True(t, trace.IsNotFound(err), "got error %T, expected a not found error due to notification invalid-id not existing", err) + + // Fetch the list again. + paginatedOut = make([]*notificationsv1.UserNotificationState, 0, 2) + for { + out, nextToken, err = service.ListUserNotificationStates(ctx, testUsername, 1, nextToken) + require.NoError(t, err) + + paginatedOut = append(paginatedOut, out...) + if nextToken == "" { + break + } + } + + // Verify that only userNotificationState2 remains. + require.Len(t, paginatedOut, 1) + require.Empty(t, cmp.Diff([]*notificationsv1.UserNotificationState{userNotificationState2}, paginatedOut, cmpOpts...)) + + // Upsert userNotificationState1 again. + _, err = service.UpsertUserNotificationState(ctx, testUsername, userNotificationState1) + require.NoError(t, err) + + // Test deleting all notification states for the user. + err = service.DeleteAllUserNotificationStatesForUser(ctx, testUsername) + require.NoError(t, err) + // Verify that the user now has no notification states. + out, nextToken, err = service.ListUserNotificationStates(ctx, testUsername, 0, "") + require.NoError(t, err) + require.Empty(t, nextToken) + require.Empty(t, out) +} + +// TestUserLastSeenNotificationCRUD tests backend operations for user last seen notification resources. +func TestUserLastSeenNotificationCRUD(t *testing.T) { + ctx := context.Background() + clock := clockwork.NewFakeClock() + + mem, err := memory.New(memory.Config{ + Context: ctx, + Clock: clock, + }) + require.NoError(t, err) + + service, err := NewNotificationsService(backend.NewSanitizer(mem), clock) + require.NoError(t, err) + + testUsername := "test-username" + testTimestamp := timestamppb.New(time.UnixMilli(1708041600000)) // February 16, 2024 12:00:00 AM UTC + + userLastSeenNotification := ¬ificationsv1.UserLastSeenNotification{ + Status: ¬ificationsv1.UserLastSeenNotificationStatus{ + LastSeenTime: testTimestamp, + }, + } + + // Initially we expect the user's last seen notification object to not exist. + _, err = service.GetUserLastSeenNotification(ctx, testUsername) + 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) + + cmpOpts := []cmp.Option{ + protocmp.IgnoreFields(&headerv1.Metadata{}, "id", "revision"), + protocmp.Transform(), + } + + // Upsert user last seen notification. + ulsn, err := service.UpsertUserLastSeenNotification(ctx, testUsername, userLastSeenNotification) + require.Empty(t, cmp.Diff(userLastSeenNotification, ulsn, cmpOpts...)) + require.NoError(t, err) + + // The user's last seen notification object should now exist. + out, err := service.GetUserLastSeenNotification(ctx, testUsername) + require.NoError(t, err) + require.Empty(t, cmp.Diff(userLastSeenNotification, out, cmpOpts...)) + + // Test deleting a user last seen notification object. + err = service.DeleteUserLastSeenNotification(ctx, testUsername) + require.NoError(t, err) + // Deleting a non-existent user last seen notification object should return an error. + err = service.DeleteUserLastSeenNotification(ctx, "invalid-username") + require.True(t, trace.IsNotFound(err), "got error %T, expected a not found error due to user_last_seen_notification for invalid-username not existing", err) + + // Getting the user's last seen notification object should now fail again since we deleted it. + _, err = service.GetUserLastSeenNotification(ctx, testUsername) + 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, description string) *notificationsv1.Notification { + t.Helper() + + notification := notificationsv1.Notification{ + SubKind: "test-subkind", + Spec: ¬ificationsv1.NotificationSpec{}, + Metadata: &headerv1.Metadata{ + Labels: map[string]string{"description": description}, + }, + } + + return ¬ification +} + +func newGlobalNotification(t *testing.T, description string) *notificationsv1.GlobalNotification { + t.Helper() + + notification := notificationsv1.GlobalNotification{ + Spec: ¬ificationsv1.GlobalNotificationSpec{ + Matcher: ¬ificationsv1.GlobalNotificationSpec_All{ + All: true, + }, + Notification: ¬ificationsv1.Notification{ + SubKind: "test-subkind", + Spec: ¬ificationsv1.NotificationSpec{}, + Metadata: &headerv1.Metadata{ + Labels: map[string]string{"description": description}, + }, + }, + }, + } + + return ¬ification +} diff --git a/lib/services/notifications.go b/lib/services/notifications.go new file mode 100644 index 0000000000000..cafee9f050a0a --- /dev/null +++ b/lib/services/notifications.go @@ -0,0 +1,307 @@ +/* + * 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 services + +import ( + "context" + + "github.com/gravitational/trace" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" + + notificationsv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/notifications/v1" + "github.com/gravitational/teleport/lib/utils" +) + +// Notifications defines an interface for managing notifications. +type Notifications interface { + ListNotificationsForUser(context.Context, notificationsv1.ListUserNotificationsRequest) ([]notificationsv1.Notification, error) + CreateUserNotification(ctx context.Context, username string, notification *notificationsv1.Notification) error + DeleteUserNotification(ctx context.Context, username string, notificationId string) error + DeleteAllUserNotificationsForUser(ctx context.Context, username string) error + CreateGlobalNotification(ctx context.Context, globalNotification *notificationsv1.GlobalNotification) (notificationsv1.GlobalNotification, error) + DeleteGlobalNotification(ctx context.Context, notificationId string) error + UpsertUserNotificationState(ctx context.Context, username string, state *notificationsv1.UserNotificationState) (notificationsv1.UserNotificationState, error) + DeleteUserNotificationState(ctx context.Context, username string, notificationId string) error + DeleteAllUserNotificationStatesForUser(ctx context.Context, username string) error + ListUserNotificationStates(ctx context.Context, username string, pageSize int, nextToken string) ([]notificationsv1.UserNotificationState, string, error) + UpsertUserLastSeenNotification(ctx context.Context, username string, ulsn *notificationsv1.UserLastSeenNotification) (notificationsv1.UserLastSeenNotification, error) + GetUserLastSeenNotification(ctx context.Context, username string) (notificationsv1.UserLastSeenNotification, error) + DeleteUserLastSeenNotification(ctx context.Context, username string) error +} + +// ValidateNotification verifies that the necessary fields are configured for a notification object. +func ValidateNotification(notification *notificationsv1.Notification) error { + if notification.SubKind == "" { + return trace.BadParameter("notification subkind is missing") + } + + if notification.Spec == nil { + return trace.BadParameter("notification spec is missing") + } + + if notification.Metadata == nil { + return trace.BadParameter("notification metadata is missing") + } + + if notification.Metadata.Labels == nil { + return trace.BadParameter("notification metadata labels are missing") + } + + return nil +} + +// MarshalNotification marshals a Notification resource to JSON. +func MarshalNotification(notification *notificationsv1.Notification, opts ...MarshalOption) ([]byte, error) { + if err := ValidateNotification(notification); err != nil { + return nil, trace.Wrap(err) + } + + cfg, err := CollectOptions(opts) + if err != nil { + return nil, trace.Wrap(err) + } + if !cfg.PreserveResourceID { + notification = proto.Clone(notification).(*notificationsv1.Notification) + //nolint:staticcheck // SA1019. Deprecated, but still needed. + notification.Metadata.Id = 0 + notification.Metadata.Revision = "" + } + data, err := utils.FastMarshal(notification) + if err != nil { + return nil, trace.Wrap(err) + } + return data, nil +} + +// UnmarshalNotification unmarshals a Notification resource from JSON. +func UnmarshalNotification(data []byte, opts ...MarshalOption) (*notificationsv1.Notification, error) { + if len(data) == 0 { + return nil, trace.BadParameter("missing notification data") + } + cfg, err := CollectOptions(opts) + if err != nil { + return nil, trace.Wrap(err) + } + var obj notificationsv1.Notification + if err = utils.FastUnmarshal(data, &obj); err != nil { + return nil, trace.Wrap(err) + } + if cfg.ID != 0 { + //nolint:staticcheck // SA1019. Id is deprecated, but still needed. + obj.Metadata.Id = cfg.ID + } + if cfg.Revision != "" { + obj.Metadata.Revision = cfg.Revision + } + if !cfg.Expires.IsZero() { + obj.Metadata.Expires = timestamppb.New(cfg.Expires) + } + + return &obj, nil +} + +// ValidateGlobalNotification verifies that the necessary fields are configured for a global notification object. +func ValidateGlobalNotification(globalNotification *notificationsv1.GlobalNotification) error { + if globalNotification.Spec == nil { + return trace.BadParameter("notification spec is missing") + } + + if globalNotification.Spec.Matcher == nil { + return trace.BadParameter("matcher is missing, a matcher is required for a global notification") + } + + if err := ValidateNotification(globalNotification.Spec.Notification); err != nil { + return trace.Wrap(err) + } + + return nil +} + +// MarshalGlobalNotification marshals a GlobalNotification resource to JSON. +func MarshalGlobalNotification(globalNotification *notificationsv1.GlobalNotification, opts ...MarshalOption) ([]byte, error) { + if err := ValidateGlobalNotification(globalNotification); err != nil { + return nil, trace.Wrap(err) + } + + cfg, err := CollectOptions(opts) + if err != nil { + return nil, trace.Wrap(err) + } + if !cfg.PreserveResourceID { + globalNotification = proto.Clone(globalNotification).(*notificationsv1.GlobalNotification) + //nolint:staticcheck // SA1019. Deprecated, but still needed. + globalNotification.Metadata.Id = 0 + globalNotification.Metadata.Revision = "" + } + // We marshal with raw protojson here because utils.FastMarshal doesn't work with oneof. + data, err := protojson.Marshal(globalNotification) + if err != nil { + return nil, trace.Wrap(err) + } + return data, nil +} + +// UnmarshalGlobalNotification unmarshals a GlobalNotification resource from JSON. +func UnmarshalGlobalNotification(data []byte, opts ...MarshalOption) (*notificationsv1.GlobalNotification, error) { + if len(data) == 0 { + return nil, trace.BadParameter("missing notification data") + } + cfg, err := CollectOptions(opts) + if err != nil { + return nil, trace.Wrap(err) + } + var obj notificationsv1.GlobalNotification + // We unmarshal with raw protojson here because utils.FastUnmarshal doesn't work with oneof. + if err = protojson.Unmarshal(data, &obj); err != nil { + return nil, trace.Wrap(err) + } + if cfg.ID != 0 { + //nolint:staticcheck // SA1019. Id is deprecated, but still needed. + obj.Metadata.Id = cfg.ID + } + if cfg.Revision != "" { + obj.Metadata.Revision = cfg.Revision + } + if !cfg.Expires.IsZero() { + obj.Metadata.Expires = timestamppb.New(cfg.Expires) + } + return &obj, nil +} + +// ValidateUserNotificationState verifies that the necessary fields are configured for user notification state object. +func ValidateUserNotificationState(notificationState *notificationsv1.UserNotificationState) error { + if notificationState.Spec.NotificationId == "" { + return trace.BadParameter("notification id is missing") + } + + if notificationState.Status == nil { + return trace.BadParameter("notification state status is missing") + } + + return nil +} + +// MarshalUserNotificationState marshals a UserNotificationState resource to JSON. +func MarshalUserNotificationState(notificationState *notificationsv1.UserNotificationState, opts ...MarshalOption) ([]byte, error) { + if err := ValidateUserNotificationState(notificationState); err != nil { + return nil, trace.Wrap(err) + } + + cfg, err := CollectOptions(opts) + if err != nil { + return nil, trace.Wrap(err) + } + if !cfg.PreserveResourceID { + notificationState = proto.Clone(notificationState).(*notificationsv1.UserNotificationState) + //nolint:staticcheck // SA1019. Deprecated, but still needed. + notificationState.Metadata.Id = 0 + notificationState.Metadata.Revision = "" + } + data, err := utils.FastMarshal(notificationState) + if err != nil { + return nil, trace.Wrap(err) + } + return data, nil +} + +// UnmarshalUserNotificationState unmarshals a UserNotificationState resource from JSON. +func UnmarshalUserNotificationState(data []byte, opts ...MarshalOption) (*notificationsv1.UserNotificationState, error) { + if len(data) == 0 { + return nil, trace.BadParameter("missing notification data") + } + cfg, err := CollectOptions(opts) + if err != nil { + return nil, trace.Wrap(err) + } + var obj notificationsv1.UserNotificationState + if err = utils.FastUnmarshal(data, &obj); err != nil { + return nil, trace.Wrap(err) + } + if cfg.ID != 0 { + //nolint:staticcheck // SA1019. Id is deprecated, but still needed. + obj.Metadata.Id = cfg.ID + } + if cfg.Revision != "" { + obj.Metadata.Revision = cfg.Revision + } + if !cfg.Expires.IsZero() { + obj.Metadata.Expires = timestamppb.New(cfg.Expires) + } + return &obj, nil +} + +// ValidateUserLastSeenNotification verifies that the necessary fields are configured for a user's last seen notification timestamp object. +func ValidateUserLastSeenNotification(lastSeenNotification *notificationsv1.UserLastSeenNotification) error { + if lastSeenNotification.Status.LastSeenTime == nil { + return trace.BadParameter("last seen time is missing") + } + + return nil +} + +// MarshalUserLastSeenNotification marshals a UserLastSeenNotification resource to JSON. +func MarshalUserLastSeenNotification(userLastSeenNotification *notificationsv1.UserLastSeenNotification, opts ...MarshalOption) ([]byte, error) { + if err := ValidateUserLastSeenNotification(userLastSeenNotification); err != nil { + return nil, trace.Wrap(err) + } + + cfg, err := CollectOptions(opts) + if err != nil { + return nil, trace.Wrap(err) + } + if !cfg.PreserveResourceID { + userLastSeenNotification = proto.Clone(userLastSeenNotification).(*notificationsv1.UserLastSeenNotification) + //nolint:staticcheck // SA1019. Deprecated, but still needed. + userLastSeenNotification.Metadata.Id = 0 + userLastSeenNotification.Metadata.Revision = "" + } + data, err := utils.FastMarshal(userLastSeenNotification) + if err != nil { + return nil, trace.Wrap(err) + } + return data, nil +} + +// UnmarshalUserLastSeenNotification unmarshals a UserLastSeenNotification resource from JSON. +func UnmarshalUserLastSeenNotification(data []byte, opts ...MarshalOption) (*notificationsv1.UserLastSeenNotification, error) { + if len(data) == 0 { + return nil, trace.BadParameter("missing notification data") + } + cfg, err := CollectOptions(opts) + if err != nil { + return nil, trace.Wrap(err) + } + var obj notificationsv1.UserLastSeenNotification + if err = utils.FastUnmarshal(data, &obj); err != nil { + return nil, trace.Wrap(err) + } + if cfg.ID != 0 { + //nolint:staticcheck // SA1019. Id is deprecated, but still needed. + obj.Metadata.Id = cfg.ID + } + if cfg.Revision != "" { + obj.Metadata.Revision = cfg.Revision + } + if !cfg.Expires.IsZero() { + obj.Metadata.Expires = timestamppb.New(cfg.Expires) + } + return &obj, nil +} diff --git a/lib/services/notifications_test.go b/lib/services/notifications_test.go new file mode 100644 index 0000000000000..70e2c26466311 --- /dev/null +++ b/lib/services/notifications_test.go @@ -0,0 +1,127 @@ +/* + * 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 services + +import ( + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/testing/protocmp" + "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" +) + +// TestMarshalNotificationRoundTrip tests the marshaling and unmarshaling functions for Notification objects. +func TestMarshalNotificationRoundTrip(t *testing.T) { + notification := ¬ificationsv1.Notification{ + Kind: types.KindNotification, + Version: types.V1, + SubKind: "test-subkind", + Spec: ¬ificationsv1.NotificationSpec{ + Id: "test-notification-1", + }, + Metadata: &headerv1.Metadata{ + Labels: map[string]string{"description": "description-1"}, + }, + } + + payload, err := MarshalNotification(notification) + require.NoError(t, err) + + unmarshaled, err := UnmarshalNotification(payload) + require.NoError(t, err) + + require.Empty(t, cmp.Diff(notification, unmarshaled, protocmp.Transform())) +} + +// TestMarshalGlobalNotificationRoundTrip tests the marshaling and unmarshaling functions for GlobalNotification objects. +func TestMarshalGlobalNotificationRoundTrip(t *testing.T) { + notification := ¬ificationsv1.GlobalNotification{ + Kind: types.KindGlobalNotification, + Metadata: &headerv1.Metadata{}, + Version: types.V1, + Spec: ¬ificationsv1.GlobalNotificationSpec{ + Matcher: ¬ificationsv1.GlobalNotificationSpec_All{ + All: true, + }, + Notification: ¬ificationsv1.Notification{ + SubKind: "test-subkind", + Spec: ¬ificationsv1.NotificationSpec{ + Id: "test-notification-id", + }, + Metadata: &headerv1.Metadata{ + Labels: map[string]string{"description": "description-1"}, + }, + }, + }, + } + + payload, err := MarshalGlobalNotification(notification) + require.NoError(t, err) + + unmarshaled, err := UnmarshalGlobalNotification(payload) + require.NoError(t, err) + + require.Empty(t, cmp.Diff(notification, unmarshaled, protocmp.Transform())) +} + +// TestUserNotificationStateRoundTrip tests the marshaling and unmarshaling functions for UserNotificationState objects. +func TestUserNotificationStateRoundTrip(t *testing.T) { + userNotificationState := ¬ificationsv1.UserNotificationState{ + Metadata: &headerv1.Metadata{}, + Spec: ¬ificationsv1.UserNotificationStateSpec{ + NotificationId: "test-notification-1", + }, + Status: ¬ificationsv1.UserNotificationStateStatus{ + NotificationState: notificationsv1.NotificationState_NOTIFICATION_STATE_CLICKED, + }, + } + + payload, err := MarshalUserNotificationState(userNotificationState) + require.NoError(t, err) + + unmarshaled, err := UnmarshalUserNotificationState(payload) + require.NoError(t, err) + + require.Empty(t, cmp.Diff(userNotificationState, unmarshaled, protocmp.Transform())) +} + +// TestUserLastSeenNotificationRoundTrip tests the marshaling and unmarshaling functions for Notification objects. +func TestUserLastSeenNotificationStateRoundTrip(t *testing.T) { + timestamp := timestamppb.New(time.UnixMilli(1708041600000)) // February 16, 2024 12:00:00 AM UTC + userLastSeenNotification := ¬ificationsv1.UserLastSeenNotification{ + Metadata: &headerv1.Metadata{}, + Status: ¬ificationsv1.UserLastSeenNotificationStatus{ + LastSeenTime: timestamp, + }, + } + + payload, err := MarshalUserLastSeenNotification(userLastSeenNotification) + require.NoError(t, err) + + unmarshaled, err := UnmarshalUserLastSeenNotification(payload) + require.NoError(t, err) + + require.Empty(t, cmp.Diff(userLastSeenNotification, unmarshaled, protocmp.Transform())) +}