From 04f239a2fc74f7090c779a6a7b8a08238fd31b88 Mon Sep 17 00:00:00 2001 From: boojack Date: Wed, 18 Mar 2026 22:42:57 +0800 Subject: [PATCH] fix(api): remove public activity service (#5734) --- proto/api/v1/activity_service.proto | 125 ---- proto/api/v1/user_service.proto | 15 +- proto/gen/api/v1/activity_service.pb.go | 623 ------------------ proto/gen/api/v1/activity_service.pb.gw.go | 243 ------- proto/gen/api/v1/activity_service_grpc.pb.go | 163 ----- .../apiv1connect/activity_service.connect.go | 142 ---- proto/gen/api/v1/user_service.pb.go | 329 +++++---- proto/gen/openapi.yaml | 163 +---- server/router/api/v1/acl_config_test.go | 2 - server/router/api/v1/activity_service.go | 175 ----- server/router/api/v1/connect_handler.go | 1 - server/router/api/v1/connect_services.go | 18 - server/router/api/v1/resource_name.go | 13 - .../api/v1/test/activity_deleted_memo_test.go | 263 -------- .../api/v1/test/activity_service_test.go | 67 -- .../api/v1/test/user_notification_test.go | 151 +++++ server/router/api/v1/user_service.go | 55 +- server/router/api/v1/v1.go | 4 - .../components/Inbox/MemoCommentMessage.tsx | 36 +- web/src/connect.ts | 2 - web/src/helpers/resource-names.ts | 1 - .../types/proto/api/v1/activity_service_pb.ts | 304 --------- web/src/types/proto/api/v1/user_service_pb.ts | 42 +- 23 files changed, 491 insertions(+), 2446 deletions(-) delete mode 100644 proto/api/v1/activity_service.proto delete mode 100644 proto/gen/api/v1/activity_service.pb.go delete mode 100644 proto/gen/api/v1/activity_service.pb.gw.go delete mode 100644 proto/gen/api/v1/activity_service_grpc.pb.go delete mode 100644 proto/gen/api/v1/apiv1connect/activity_service.connect.go delete mode 100644 server/router/api/v1/activity_service.go delete mode 100644 server/router/api/v1/test/activity_deleted_memo_test.go delete mode 100644 server/router/api/v1/test/activity_service_test.go create mode 100644 server/router/api/v1/test/user_notification_test.go delete mode 100644 web/src/types/proto/api/v1/activity_service_pb.ts diff --git a/proto/api/v1/activity_service.proto b/proto/api/v1/activity_service.proto deleted file mode 100644 index 97e2b91cb..000000000 --- a/proto/api/v1/activity_service.proto +++ /dev/null @@ -1,125 +0,0 @@ -syntax = "proto3"; - -package memos.api.v1; - -import "google/api/annotations.proto"; -import "google/api/client.proto"; -import "google/api/field_behavior.proto"; -import "google/api/resource.proto"; -import "google/protobuf/timestamp.proto"; - -option go_package = "gen/api/v1"; - -service ActivityService { - // ListActivities returns a list of activities. - rpc ListActivities(ListActivitiesRequest) returns (ListActivitiesResponse) { - option (google.api.http) = {get: "/api/v1/activities"}; - } - - // GetActivity returns the activity with the given id. - rpc GetActivity(GetActivityRequest) returns (Activity) { - option (google.api.http) = {get: "/api/v1/{name=activities/*}"}; - option (google.api.method_signature) = "name"; - } -} - -message Activity { - option (google.api.resource) = { - type: "memos.api.v1/Activity" - pattern: "activities/{activity}" - name_field: "name" - singular: "activity" - plural: "activities" - }; - - // The name of the activity. - // Format: activities/{id} - string name = 1 [ - (google.api.field_behavior) = OUTPUT_ONLY, - (google.api.field_behavior) = IDENTIFIER - ]; - - // The name of the creator. - // Format: users/{user} - string creator = 2 [(google.api.field_behavior) = OUTPUT_ONLY]; - - // The type of the activity. - Type type = 3 [(google.api.field_behavior) = OUTPUT_ONLY]; - - // The level of the activity. - Level level = 4 [(google.api.field_behavior) = OUTPUT_ONLY]; - - // The create time of the activity. - google.protobuf.Timestamp create_time = 5 [(google.api.field_behavior) = OUTPUT_ONLY]; - - // The payload of the activity. - ActivityPayload payload = 6 [(google.api.field_behavior) = OUTPUT_ONLY]; - - // Activity types. - enum Type { - // Unspecified type. - TYPE_UNSPECIFIED = 0; - // Memo comment activity. - MEMO_COMMENT = 1; - } - - // Activity levels. - enum Level { - // Unspecified level. - LEVEL_UNSPECIFIED = 0; - // Info level. - INFO = 1; - // Warn level. - WARN = 2; - // Error level. - ERROR = 3; - } -} - -message ActivityPayload { - oneof payload { - // Memo comment activity payload. - ActivityMemoCommentPayload memo_comment = 1; - } -} - -// ActivityMemoCommentPayload represents the payload of a memo comment activity. -message ActivityMemoCommentPayload { - // The memo name of comment. - // Format: memos/{memo} - string memo = 1; - // The name of related memo. - // Format: memos/{memo} - string related_memo = 2; -} - -message ListActivitiesRequest { - // The maximum number of activities to return. - // The service may return fewer than this value. - // If unspecified, at most 100 activities will be returned. - // The maximum value is 1000; values above 1000 will be coerced to 1000. - int32 page_size = 1; - - // A page token, received from a previous `ListActivities` call. - // Provide this to retrieve the subsequent page. - string page_token = 2; -} - -message ListActivitiesResponse { - // The activities. - repeated Activity activities = 1; - - // A token to retrieve the next page of results. - // Pass this value in the page_token field in the subsequent call to `ListActivities` - // method to retrieve the next page of results. - string next_page_token = 2; -} - -message GetActivityRequest { - // The name of the activity. - // Format: activities/{id}, id is the system generated auto-incremented id. - string name = 1 [ - (google.api.field_behavior) = REQUIRED, - (google.api.resource_reference) = {type: "memos.api.v1/Activity"} - ]; -} diff --git a/proto/api/v1/user_service.proto b/proto/api/v1/user_service.proto index 53883acbb..e30a5cccd 100644 --- a/proto/api/v1/user_service.proto +++ b/proto/api/v1/user_service.proto @@ -626,8 +626,19 @@ message UserNotification { // The type of the notification. Type type = 5 [(google.api.field_behavior) = OUTPUT_ONLY]; - // The activity ID associated with this notification. - optional int32 activity_id = 6 [(google.api.field_behavior) = OPTIONAL]; + oneof payload { + MemoCommentPayload memo_comment = 6 [(google.api.field_behavior) = OUTPUT_ONLY]; + } + + message MemoCommentPayload { + // The memo name of comment. + // Format: memos/{memo} + string memo = 1; + + // The name of related memo. + // Format: memos/{memo} + string related_memo = 2; + } enum Status { STATUS_UNSPECIFIED = 0; diff --git a/proto/gen/api/v1/activity_service.pb.go b/proto/gen/api/v1/activity_service.pb.go deleted file mode 100644 index a938f47a9..000000000 --- a/proto/gen/api/v1/activity_service.pb.go +++ /dev/null @@ -1,623 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.11 -// protoc (unknown) -// source: api/v1/activity_service.proto - -package apiv1 - -import ( - _ "google.golang.org/genproto/googleapis/api/annotations" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - timestamppb "google.golang.org/protobuf/types/known/timestamppb" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// Activity types. -type Activity_Type int32 - -const ( - // Unspecified type. - Activity_TYPE_UNSPECIFIED Activity_Type = 0 - // Memo comment activity. - Activity_MEMO_COMMENT Activity_Type = 1 -) - -// Enum value maps for Activity_Type. -var ( - Activity_Type_name = map[int32]string{ - 0: "TYPE_UNSPECIFIED", - 1: "MEMO_COMMENT", - } - Activity_Type_value = map[string]int32{ - "TYPE_UNSPECIFIED": 0, - "MEMO_COMMENT": 1, - } -) - -func (x Activity_Type) Enum() *Activity_Type { - p := new(Activity_Type) - *p = x - return p -} - -func (x Activity_Type) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (Activity_Type) Descriptor() protoreflect.EnumDescriptor { - return file_api_v1_activity_service_proto_enumTypes[0].Descriptor() -} - -func (Activity_Type) Type() protoreflect.EnumType { - return &file_api_v1_activity_service_proto_enumTypes[0] -} - -func (x Activity_Type) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use Activity_Type.Descriptor instead. -func (Activity_Type) EnumDescriptor() ([]byte, []int) { - return file_api_v1_activity_service_proto_rawDescGZIP(), []int{0, 0} -} - -// Activity levels. -type Activity_Level int32 - -const ( - // Unspecified level. - Activity_LEVEL_UNSPECIFIED Activity_Level = 0 - // Info level. - Activity_INFO Activity_Level = 1 - // Warn level. - Activity_WARN Activity_Level = 2 - // Error level. - Activity_ERROR Activity_Level = 3 -) - -// Enum value maps for Activity_Level. -var ( - Activity_Level_name = map[int32]string{ - 0: "LEVEL_UNSPECIFIED", - 1: "INFO", - 2: "WARN", - 3: "ERROR", - } - Activity_Level_value = map[string]int32{ - "LEVEL_UNSPECIFIED": 0, - "INFO": 1, - "WARN": 2, - "ERROR": 3, - } -) - -func (x Activity_Level) Enum() *Activity_Level { - p := new(Activity_Level) - *p = x - return p -} - -func (x Activity_Level) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (Activity_Level) Descriptor() protoreflect.EnumDescriptor { - return file_api_v1_activity_service_proto_enumTypes[1].Descriptor() -} - -func (Activity_Level) Type() protoreflect.EnumType { - return &file_api_v1_activity_service_proto_enumTypes[1] -} - -func (x Activity_Level) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use Activity_Level.Descriptor instead. -func (Activity_Level) EnumDescriptor() ([]byte, []int) { - return file_api_v1_activity_service_proto_rawDescGZIP(), []int{0, 1} -} - -type Activity struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The name of the activity. - // Format: activities/{id} - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // The name of the creator. - // Format: users/{user} - Creator string `protobuf:"bytes,2,opt,name=creator,proto3" json:"creator,omitempty"` - // The type of the activity. - Type Activity_Type `protobuf:"varint,3,opt,name=type,proto3,enum=memos.api.v1.Activity_Type" json:"type,omitempty"` - // The level of the activity. - Level Activity_Level `protobuf:"varint,4,opt,name=level,proto3,enum=memos.api.v1.Activity_Level" json:"level,omitempty"` - // The create time of the activity. - CreateTime *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty"` - // The payload of the activity. - Payload *ActivityPayload `protobuf:"bytes,6,opt,name=payload,proto3" json:"payload,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *Activity) Reset() { - *x = Activity{} - mi := &file_api_v1_activity_service_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Activity) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Activity) ProtoMessage() {} - -func (x *Activity) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_activity_service_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Activity.ProtoReflect.Descriptor instead. -func (*Activity) Descriptor() ([]byte, []int) { - return file_api_v1_activity_service_proto_rawDescGZIP(), []int{0} -} - -func (x *Activity) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *Activity) GetCreator() string { - if x != nil { - return x.Creator - } - return "" -} - -func (x *Activity) GetType() Activity_Type { - if x != nil { - return x.Type - } - return Activity_TYPE_UNSPECIFIED -} - -func (x *Activity) GetLevel() Activity_Level { - if x != nil { - return x.Level - } - return Activity_LEVEL_UNSPECIFIED -} - -func (x *Activity) GetCreateTime() *timestamppb.Timestamp { - if x != nil { - return x.CreateTime - } - return nil -} - -func (x *Activity) GetPayload() *ActivityPayload { - if x != nil { - return x.Payload - } - return nil -} - -type ActivityPayload struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Payload: - // - // *ActivityPayload_MemoComment - Payload isActivityPayload_Payload `protobuf_oneof:"payload"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ActivityPayload) Reset() { - *x = ActivityPayload{} - mi := &file_api_v1_activity_service_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ActivityPayload) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ActivityPayload) ProtoMessage() {} - -func (x *ActivityPayload) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_activity_service_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ActivityPayload.ProtoReflect.Descriptor instead. -func (*ActivityPayload) Descriptor() ([]byte, []int) { - return file_api_v1_activity_service_proto_rawDescGZIP(), []int{1} -} - -func (x *ActivityPayload) GetPayload() isActivityPayload_Payload { - if x != nil { - return x.Payload - } - return nil -} - -func (x *ActivityPayload) GetMemoComment() *ActivityMemoCommentPayload { - if x != nil { - if x, ok := x.Payload.(*ActivityPayload_MemoComment); ok { - return x.MemoComment - } - } - return nil -} - -type isActivityPayload_Payload interface { - isActivityPayload_Payload() -} - -type ActivityPayload_MemoComment struct { - // Memo comment activity payload. - MemoComment *ActivityMemoCommentPayload `protobuf:"bytes,1,opt,name=memo_comment,json=memoComment,proto3,oneof"` -} - -func (*ActivityPayload_MemoComment) isActivityPayload_Payload() {} - -// ActivityMemoCommentPayload represents the payload of a memo comment activity. -type ActivityMemoCommentPayload struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The memo name of comment. - // Format: memos/{memo} - Memo string `protobuf:"bytes,1,opt,name=memo,proto3" json:"memo,omitempty"` - // The name of related memo. - // Format: memos/{memo} - RelatedMemo string `protobuf:"bytes,2,opt,name=related_memo,json=relatedMemo,proto3" json:"related_memo,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ActivityMemoCommentPayload) Reset() { - *x = ActivityMemoCommentPayload{} - mi := &file_api_v1_activity_service_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ActivityMemoCommentPayload) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ActivityMemoCommentPayload) ProtoMessage() {} - -func (x *ActivityMemoCommentPayload) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_activity_service_proto_msgTypes[2] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ActivityMemoCommentPayload.ProtoReflect.Descriptor instead. -func (*ActivityMemoCommentPayload) Descriptor() ([]byte, []int) { - return file_api_v1_activity_service_proto_rawDescGZIP(), []int{2} -} - -func (x *ActivityMemoCommentPayload) GetMemo() string { - if x != nil { - return x.Memo - } - return "" -} - -func (x *ActivityMemoCommentPayload) GetRelatedMemo() string { - if x != nil { - return x.RelatedMemo - } - return "" -} - -type ListActivitiesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The maximum number of activities to return. - // The service may return fewer than this value. - // If unspecified, at most 100 activities will be returned. - // The maximum value is 1000; values above 1000 will be coerced to 1000. - PageSize int32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` - // A page token, received from a previous `ListActivities` call. - // Provide this to retrieve the subsequent page. - PageToken string `protobuf:"bytes,2,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListActivitiesRequest) Reset() { - *x = ListActivitiesRequest{} - mi := &file_api_v1_activity_service_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListActivitiesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListActivitiesRequest) ProtoMessage() {} - -func (x *ListActivitiesRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_activity_service_proto_msgTypes[3] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListActivitiesRequest.ProtoReflect.Descriptor instead. -func (*ListActivitiesRequest) Descriptor() ([]byte, []int) { - return file_api_v1_activity_service_proto_rawDescGZIP(), []int{3} -} - -func (x *ListActivitiesRequest) GetPageSize() int32 { - if x != nil { - return x.PageSize - } - return 0 -} - -func (x *ListActivitiesRequest) GetPageToken() string { - if x != nil { - return x.PageToken - } - return "" -} - -type ListActivitiesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The activities. - Activities []*Activity `protobuf:"bytes,1,rep,name=activities,proto3" json:"activities,omitempty"` - // A token to retrieve the next page of results. - // Pass this value in the page_token field in the subsequent call to `ListActivities` - // method to retrieve the next page of results. - NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListActivitiesResponse) Reset() { - *x = ListActivitiesResponse{} - mi := &file_api_v1_activity_service_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListActivitiesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListActivitiesResponse) ProtoMessage() {} - -func (x *ListActivitiesResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_activity_service_proto_msgTypes[4] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListActivitiesResponse.ProtoReflect.Descriptor instead. -func (*ListActivitiesResponse) Descriptor() ([]byte, []int) { - return file_api_v1_activity_service_proto_rawDescGZIP(), []int{4} -} - -func (x *ListActivitiesResponse) GetActivities() []*Activity { - if x != nil { - return x.Activities - } - return nil -} - -func (x *ListActivitiesResponse) GetNextPageToken() string { - if x != nil { - return x.NextPageToken - } - return "" -} - -type GetActivityRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The name of the activity. - // Format: activities/{id}, id is the system generated auto-incremented id. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetActivityRequest) Reset() { - *x = GetActivityRequest{} - mi := &file_api_v1_activity_service_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetActivityRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetActivityRequest) ProtoMessage() {} - -func (x *GetActivityRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_activity_service_proto_msgTypes[5] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetActivityRequest.ProtoReflect.Descriptor instead. -func (*GetActivityRequest) Descriptor() ([]byte, []int) { - return file_api_v1_activity_service_proto_rawDescGZIP(), []int{5} -} - -func (x *GetActivityRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -var File_api_v1_activity_service_proto protoreflect.FileDescriptor - -const file_api_v1_activity_service_proto_rawDesc = "" + - "\n" + - "\x1dapi/v1/activity_service.proto\x12\fmemos.api.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xf2\x03\n" + - "\bActivity\x12\x1a\n" + - "\x04name\x18\x01 \x01(\tB\x06\xe0A\x03\xe0A\bR\x04name\x12\x1d\n" + - "\acreator\x18\x02 \x01(\tB\x03\xe0A\x03R\acreator\x124\n" + - "\x04type\x18\x03 \x01(\x0e2\x1b.memos.api.v1.Activity.TypeB\x03\xe0A\x03R\x04type\x127\n" + - "\x05level\x18\x04 \x01(\x0e2\x1c.memos.api.v1.Activity.LevelB\x03\xe0A\x03R\x05level\x12@\n" + - "\vcreate_time\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampB\x03\xe0A\x03R\n" + - "createTime\x12<\n" + - "\apayload\x18\x06 \x01(\v2\x1d.memos.api.v1.ActivityPayloadB\x03\xe0A\x03R\apayload\".\n" + - "\x04Type\x12\x14\n" + - "\x10TYPE_UNSPECIFIED\x10\x00\x12\x10\n" + - "\fMEMO_COMMENT\x10\x01\"=\n" + - "\x05Level\x12\x15\n" + - "\x11LEVEL_UNSPECIFIED\x10\x00\x12\b\n" + - "\x04INFO\x10\x01\x12\b\n" + - "\x04WARN\x10\x02\x12\t\n" + - "\x05ERROR\x10\x03:M\xeaAJ\n" + - "\x15memos.api.v1/Activity\x12\x15activities/{activity}\x1a\x04name*\n" + - "activities2\bactivity\"k\n" + - "\x0fActivityPayload\x12M\n" + - "\fmemo_comment\x18\x01 \x01(\v2(.memos.api.v1.ActivityMemoCommentPayloadH\x00R\vmemoCommentB\t\n" + - "\apayload\"S\n" + - "\x1aActivityMemoCommentPayload\x12\x12\n" + - "\x04memo\x18\x01 \x01(\tR\x04memo\x12!\n" + - "\frelated_memo\x18\x02 \x01(\tR\vrelatedMemo\"S\n" + - "\x15ListActivitiesRequest\x12\x1b\n" + - "\tpage_size\x18\x01 \x01(\x05R\bpageSize\x12\x1d\n" + - "\n" + - "page_token\x18\x02 \x01(\tR\tpageToken\"x\n" + - "\x16ListActivitiesResponse\x126\n" + - "\n" + - "activities\x18\x01 \x03(\v2\x16.memos.api.v1.ActivityR\n" + - "activities\x12&\n" + - "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"G\n" + - "\x12GetActivityRequest\x121\n" + - "\x04name\x18\x01 \x01(\tB\x1d\xe0A\x02\xfaA\x17\n" + - "\x15memos.api.v1/ActivityR\x04name2\xff\x01\n" + - "\x0fActivityService\x12w\n" + - "\x0eListActivities\x12#.memos.api.v1.ListActivitiesRequest\x1a$.memos.api.v1.ListActivitiesResponse\"\x1a\x82\xd3\xe4\x93\x02\x14\x12\x12/api/v1/activities\x12s\n" + - "\vGetActivity\x12 .memos.api.v1.GetActivityRequest\x1a\x16.memos.api.v1.Activity\"*\xdaA\x04name\x82\xd3\xe4\x93\x02\x1d\x12\x1b/api/v1/{name=activities/*}B\xac\x01\n" + - "\x10com.memos.api.v1B\x14ActivityServiceProtoP\x01Z0github.com/usememos/memos/proto/gen/api/v1;apiv1\xa2\x02\x03MAX\xaa\x02\fMemos.Api.V1\xca\x02\fMemos\\Api\\V1\xe2\x02\x18Memos\\Api\\V1\\GPBMetadata\xea\x02\x0eMemos::Api::V1b\x06proto3" - -var ( - file_api_v1_activity_service_proto_rawDescOnce sync.Once - file_api_v1_activity_service_proto_rawDescData []byte -) - -func file_api_v1_activity_service_proto_rawDescGZIP() []byte { - file_api_v1_activity_service_proto_rawDescOnce.Do(func() { - file_api_v1_activity_service_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_api_v1_activity_service_proto_rawDesc), len(file_api_v1_activity_service_proto_rawDesc))) - }) - return file_api_v1_activity_service_proto_rawDescData -} - -var file_api_v1_activity_service_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_api_v1_activity_service_proto_msgTypes = make([]protoimpl.MessageInfo, 6) -var file_api_v1_activity_service_proto_goTypes = []any{ - (Activity_Type)(0), // 0: memos.api.v1.Activity.Type - (Activity_Level)(0), // 1: memos.api.v1.Activity.Level - (*Activity)(nil), // 2: memos.api.v1.Activity - (*ActivityPayload)(nil), // 3: memos.api.v1.ActivityPayload - (*ActivityMemoCommentPayload)(nil), // 4: memos.api.v1.ActivityMemoCommentPayload - (*ListActivitiesRequest)(nil), // 5: memos.api.v1.ListActivitiesRequest - (*ListActivitiesResponse)(nil), // 6: memos.api.v1.ListActivitiesResponse - (*GetActivityRequest)(nil), // 7: memos.api.v1.GetActivityRequest - (*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp -} -var file_api_v1_activity_service_proto_depIdxs = []int32{ - 0, // 0: memos.api.v1.Activity.type:type_name -> memos.api.v1.Activity.Type - 1, // 1: memos.api.v1.Activity.level:type_name -> memos.api.v1.Activity.Level - 8, // 2: memos.api.v1.Activity.create_time:type_name -> google.protobuf.Timestamp - 3, // 3: memos.api.v1.Activity.payload:type_name -> memos.api.v1.ActivityPayload - 4, // 4: memos.api.v1.ActivityPayload.memo_comment:type_name -> memos.api.v1.ActivityMemoCommentPayload - 2, // 5: memos.api.v1.ListActivitiesResponse.activities:type_name -> memos.api.v1.Activity - 5, // 6: memos.api.v1.ActivityService.ListActivities:input_type -> memos.api.v1.ListActivitiesRequest - 7, // 7: memos.api.v1.ActivityService.GetActivity:input_type -> memos.api.v1.GetActivityRequest - 6, // 8: memos.api.v1.ActivityService.ListActivities:output_type -> memos.api.v1.ListActivitiesResponse - 2, // 9: memos.api.v1.ActivityService.GetActivity:output_type -> memos.api.v1.Activity - 8, // [8:10] is the sub-list for method output_type - 6, // [6:8] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name -} - -func init() { file_api_v1_activity_service_proto_init() } -func file_api_v1_activity_service_proto_init() { - if File_api_v1_activity_service_proto != nil { - return - } - file_api_v1_activity_service_proto_msgTypes[1].OneofWrappers = []any{ - (*ActivityPayload_MemoComment)(nil), - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_v1_activity_service_proto_rawDesc), len(file_api_v1_activity_service_proto_rawDesc)), - NumEnums: 2, - NumMessages: 6, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_api_v1_activity_service_proto_goTypes, - DependencyIndexes: file_api_v1_activity_service_proto_depIdxs, - EnumInfos: file_api_v1_activity_service_proto_enumTypes, - MessageInfos: file_api_v1_activity_service_proto_msgTypes, - }.Build() - File_api_v1_activity_service_proto = out.File - file_api_v1_activity_service_proto_goTypes = nil - file_api_v1_activity_service_proto_depIdxs = nil -} diff --git a/proto/gen/api/v1/activity_service.pb.gw.go b/proto/gen/api/v1/activity_service.pb.gw.go deleted file mode 100644 index 2aba32b20..000000000 --- a/proto/gen/api/v1/activity_service.pb.gw.go +++ /dev/null @@ -1,243 +0,0 @@ -// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: api/v1/activity_service.proto - -/* -Package apiv1 is a reverse proxy. - -It translates gRPC into RESTful JSON APIs. -*/ -package apiv1 - -import ( - "context" - "errors" - "io" - "net/http" - - "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" - "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/proto" -) - -// Suppress "imported and not used" errors -var ( - _ codes.Code - _ io.Reader - _ status.Status - _ = errors.New - _ = runtime.String - _ = utilities.NewDoubleArray - _ = metadata.Join -) - -var filter_ActivityService_ListActivities_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} - -func request_ActivityService_ListActivities_0(ctx context.Context, marshaler runtime.Marshaler, client ActivityServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var ( - protoReq ListActivitiesRequest - metadata runtime.ServerMetadata - ) - if req.Body != nil { - _, _ = io.Copy(io.Discard, req.Body) - } - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ActivityService_ListActivities_0); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - msg, err := client.ListActivities(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err -} - -func local_request_ActivityService_ListActivities_0(ctx context.Context, marshaler runtime.Marshaler, server ActivityServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var ( - protoReq ListActivitiesRequest - metadata runtime.ServerMetadata - ) - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ActivityService_ListActivities_0); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - msg, err := server.ListActivities(ctx, &protoReq) - return msg, metadata, err -} - -func request_ActivityService_GetActivity_0(ctx context.Context, marshaler runtime.Marshaler, client ActivityServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var ( - protoReq GetActivityRequest - metadata runtime.ServerMetadata - err error - ) - if req.Body != nil { - _, _ = io.Copy(io.Discard, req.Body) - } - val, ok := pathParams["name"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") - } - protoReq.Name, err = runtime.String(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) - } - msg, err := client.GetActivity(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err -} - -func local_request_ActivityService_GetActivity_0(ctx context.Context, marshaler runtime.Marshaler, server ActivityServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var ( - protoReq GetActivityRequest - metadata runtime.ServerMetadata - err error - ) - val, ok := pathParams["name"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") - } - protoReq.Name, err = runtime.String(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) - } - msg, err := server.GetActivity(ctx, &protoReq) - return msg, metadata, err -} - -// RegisterActivityServiceHandlerServer registers the http handlers for service ActivityService to "mux". -// UnaryRPC :call ActivityServiceServer directly. -// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterActivityServiceHandlerFromEndpoint instead. -// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call. -func RegisterActivityServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server ActivityServiceServer) error { - mux.Handle(http.MethodGet, pattern_ActivityService_ListActivities_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - var stream runtime.ServerTransportStream - ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.ActivityService/ListActivities", runtime.WithHTTPPathPattern("/api/v1/activities")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_ActivityService_ListActivities_0(annotatedContext, inboundMarshaler, server, req, pathParams) - md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) - annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) - if err != nil { - runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) - return - } - forward_ActivityService_ListActivities_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - }) - mux.Handle(http.MethodGet, pattern_ActivityService_GetActivity_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - var stream runtime.ServerTransportStream - ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.ActivityService/GetActivity", runtime.WithHTTPPathPattern("/api/v1/{name=activities/*}")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_ActivityService_GetActivity_0(annotatedContext, inboundMarshaler, server, req, pathParams) - md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) - annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) - if err != nil { - runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) - return - } - forward_ActivityService_GetActivity_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - }) - - return nil -} - -// RegisterActivityServiceHandlerFromEndpoint is same as RegisterActivityServiceHandler but -// automatically dials to "endpoint" and closes the connection when "ctx" gets done. -func RegisterActivityServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.NewClient(endpoint, opts...) - if err != nil { - return err - } - defer func() { - if err != nil { - if cerr := conn.Close(); cerr != nil { - grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr) - } - return - } - go func() { - <-ctx.Done() - if cerr := conn.Close(); cerr != nil { - grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr) - } - }() - }() - return RegisterActivityServiceHandler(ctx, mux, conn) -} - -// RegisterActivityServiceHandler registers the http handlers for service ActivityService to "mux". -// The handlers forward requests to the grpc endpoint over "conn". -func RegisterActivityServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { - return RegisterActivityServiceHandlerClient(ctx, mux, NewActivityServiceClient(conn)) -} - -// RegisterActivityServiceHandlerClient registers the http handlers for service ActivityService -// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "ActivityServiceClient". -// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "ActivityServiceClient" -// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in -// "ActivityServiceClient" to call the correct interceptors. This client ignores the HTTP middlewares. -func RegisterActivityServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client ActivityServiceClient) error { - mux.Handle(http.MethodGet, pattern_ActivityService_ListActivities_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.ActivityService/ListActivities", runtime.WithHTTPPathPattern("/api/v1/activities")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_ActivityService_ListActivities_0(annotatedContext, inboundMarshaler, client, req, pathParams) - annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) - if err != nil { - runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) - return - } - forward_ActivityService_ListActivities_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - }) - mux.Handle(http.MethodGet, pattern_ActivityService_GetActivity_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.ActivityService/GetActivity", runtime.WithHTTPPathPattern("/api/v1/{name=activities/*}")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_ActivityService_GetActivity_0(annotatedContext, inboundMarshaler, client, req, pathParams) - annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) - if err != nil { - runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) - return - } - forward_ActivityService_GetActivity_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - }) - return nil -} - -var ( - pattern_ActivityService_ListActivities_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "activities"}, "")) - pattern_ActivityService_GetActivity_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "activities", "name"}, "")) -) - -var ( - forward_ActivityService_ListActivities_0 = runtime.ForwardResponseMessage - forward_ActivityService_GetActivity_0 = runtime.ForwardResponseMessage -) diff --git a/proto/gen/api/v1/activity_service_grpc.pb.go b/proto/gen/api/v1/activity_service_grpc.pb.go deleted file mode 100644 index 27b4d7da9..000000000 --- a/proto/gen/api/v1/activity_service_grpc.pb.go +++ /dev/null @@ -1,163 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.6.1 -// - protoc (unknown) -// source: api/v1/activity_service.proto - -package apiv1 - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 - -const ( - ActivityService_ListActivities_FullMethodName = "/memos.api.v1.ActivityService/ListActivities" - ActivityService_GetActivity_FullMethodName = "/memos.api.v1.ActivityService/GetActivity" -) - -// ActivityServiceClient is the client API for ActivityService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type ActivityServiceClient interface { - // ListActivities returns a list of activities. - ListActivities(ctx context.Context, in *ListActivitiesRequest, opts ...grpc.CallOption) (*ListActivitiesResponse, error) - // GetActivity returns the activity with the given id. - GetActivity(ctx context.Context, in *GetActivityRequest, opts ...grpc.CallOption) (*Activity, error) -} - -type activityServiceClient struct { - cc grpc.ClientConnInterface -} - -func NewActivityServiceClient(cc grpc.ClientConnInterface) ActivityServiceClient { - return &activityServiceClient{cc} -} - -func (c *activityServiceClient) ListActivities(ctx context.Context, in *ListActivitiesRequest, opts ...grpc.CallOption) (*ListActivitiesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ListActivitiesResponse) - err := c.cc.Invoke(ctx, ActivityService_ListActivities_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *activityServiceClient) GetActivity(ctx context.Context, in *GetActivityRequest, opts ...grpc.CallOption) (*Activity, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(Activity) - err := c.cc.Invoke(ctx, ActivityService_GetActivity_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -// ActivityServiceServer is the server API for ActivityService service. -// All implementations must embed UnimplementedActivityServiceServer -// for forward compatibility. -type ActivityServiceServer interface { - // ListActivities returns a list of activities. - ListActivities(context.Context, *ListActivitiesRequest) (*ListActivitiesResponse, error) - // GetActivity returns the activity with the given id. - GetActivity(context.Context, *GetActivityRequest) (*Activity, error) - mustEmbedUnimplementedActivityServiceServer() -} - -// UnimplementedActivityServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedActivityServiceServer struct{} - -func (UnimplementedActivityServiceServer) ListActivities(context.Context, *ListActivitiesRequest) (*ListActivitiesResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ListActivities not implemented") -} -func (UnimplementedActivityServiceServer) GetActivity(context.Context, *GetActivityRequest) (*Activity, error) { - return nil, status.Error(codes.Unimplemented, "method GetActivity not implemented") -} -func (UnimplementedActivityServiceServer) mustEmbedUnimplementedActivityServiceServer() {} -func (UnimplementedActivityServiceServer) testEmbeddedByValue() {} - -// UnsafeActivityServiceServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to ActivityServiceServer will -// result in compilation errors. -type UnsafeActivityServiceServer interface { - mustEmbedUnimplementedActivityServiceServer() -} - -func RegisterActivityServiceServer(s grpc.ServiceRegistrar, srv ActivityServiceServer) { - // If the following call panics, it indicates UnimplementedActivityServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } - s.RegisterService(&ActivityService_ServiceDesc, srv) -} - -func _ActivityService_ListActivities_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListActivitiesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ActivityServiceServer).ListActivities(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: ActivityService_ListActivities_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ActivityServiceServer).ListActivities(ctx, req.(*ListActivitiesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _ActivityService_GetActivity_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetActivityRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ActivityServiceServer).GetActivity(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: ActivityService_GetActivity_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ActivityServiceServer).GetActivity(ctx, req.(*GetActivityRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// ActivityService_ServiceDesc is the grpc.ServiceDesc for ActivityService service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var ActivityService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "memos.api.v1.ActivityService", - HandlerType: (*ActivityServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "ListActivities", - Handler: _ActivityService_ListActivities_Handler, - }, - { - MethodName: "GetActivity", - Handler: _ActivityService_GetActivity_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "api/v1/activity_service.proto", -} diff --git a/proto/gen/api/v1/apiv1connect/activity_service.connect.go b/proto/gen/api/v1/apiv1connect/activity_service.connect.go deleted file mode 100644 index 903efe925..000000000 --- a/proto/gen/api/v1/apiv1connect/activity_service.connect.go +++ /dev/null @@ -1,142 +0,0 @@ -// Code generated by protoc-gen-connect-go. DO NOT EDIT. -// -// Source: api/v1/activity_service.proto - -package apiv1connect - -import ( - connect "connectrpc.com/connect" - context "context" - errors "errors" - v1 "github.com/usememos/memos/proto/gen/api/v1" - http "net/http" - strings "strings" -) - -// This is a compile-time assertion to ensure that this generated file and the connect package are -// compatible. If you get a compiler error that this constant is not defined, this code was -// generated with a version of connect newer than the one compiled into your binary. You can fix the -// problem by either regenerating this code with an older version of connect or updating the connect -// version compiled into your binary. -const _ = connect.IsAtLeastVersion1_13_0 - -const ( - // ActivityServiceName is the fully-qualified name of the ActivityService service. - ActivityServiceName = "memos.api.v1.ActivityService" -) - -// These constants are the fully-qualified names of the RPCs defined in this package. They're -// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. -// -// Note that these are different from the fully-qualified method names used by -// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to -// reflection-formatted method names, remove the leading slash and convert the remaining slash to a -// period. -const ( - // ActivityServiceListActivitiesProcedure is the fully-qualified name of the ActivityService's - // ListActivities RPC. - ActivityServiceListActivitiesProcedure = "/memos.api.v1.ActivityService/ListActivities" - // ActivityServiceGetActivityProcedure is the fully-qualified name of the ActivityService's - // GetActivity RPC. - ActivityServiceGetActivityProcedure = "/memos.api.v1.ActivityService/GetActivity" -) - -// ActivityServiceClient is a client for the memos.api.v1.ActivityService service. -type ActivityServiceClient interface { - // ListActivities returns a list of activities. - ListActivities(context.Context, *connect.Request[v1.ListActivitiesRequest]) (*connect.Response[v1.ListActivitiesResponse], error) - // GetActivity returns the activity with the given id. - GetActivity(context.Context, *connect.Request[v1.GetActivityRequest]) (*connect.Response[v1.Activity], error) -} - -// NewActivityServiceClient constructs a client for the memos.api.v1.ActivityService service. By -// default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, -// and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the -// connect.WithGRPC() or connect.WithGRPCWeb() options. -// -// The URL supplied here should be the base URL for the Connect or gRPC server (for example, -// http://api.acme.com or https://acme.com/grpc). -func NewActivityServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) ActivityServiceClient { - baseURL = strings.TrimRight(baseURL, "/") - activityServiceMethods := v1.File_api_v1_activity_service_proto.Services().ByName("ActivityService").Methods() - return &activityServiceClient{ - listActivities: connect.NewClient[v1.ListActivitiesRequest, v1.ListActivitiesResponse]( - httpClient, - baseURL+ActivityServiceListActivitiesProcedure, - connect.WithSchema(activityServiceMethods.ByName("ListActivities")), - connect.WithClientOptions(opts...), - ), - getActivity: connect.NewClient[v1.GetActivityRequest, v1.Activity]( - httpClient, - baseURL+ActivityServiceGetActivityProcedure, - connect.WithSchema(activityServiceMethods.ByName("GetActivity")), - connect.WithClientOptions(opts...), - ), - } -} - -// activityServiceClient implements ActivityServiceClient. -type activityServiceClient struct { - listActivities *connect.Client[v1.ListActivitiesRequest, v1.ListActivitiesResponse] - getActivity *connect.Client[v1.GetActivityRequest, v1.Activity] -} - -// ListActivities calls memos.api.v1.ActivityService.ListActivities. -func (c *activityServiceClient) ListActivities(ctx context.Context, req *connect.Request[v1.ListActivitiesRequest]) (*connect.Response[v1.ListActivitiesResponse], error) { - return c.listActivities.CallUnary(ctx, req) -} - -// GetActivity calls memos.api.v1.ActivityService.GetActivity. -func (c *activityServiceClient) GetActivity(ctx context.Context, req *connect.Request[v1.GetActivityRequest]) (*connect.Response[v1.Activity], error) { - return c.getActivity.CallUnary(ctx, req) -} - -// ActivityServiceHandler is an implementation of the memos.api.v1.ActivityService service. -type ActivityServiceHandler interface { - // ListActivities returns a list of activities. - ListActivities(context.Context, *connect.Request[v1.ListActivitiesRequest]) (*connect.Response[v1.ListActivitiesResponse], error) - // GetActivity returns the activity with the given id. - GetActivity(context.Context, *connect.Request[v1.GetActivityRequest]) (*connect.Response[v1.Activity], error) -} - -// NewActivityServiceHandler builds an HTTP handler from the service implementation. It returns the -// path on which to mount the handler and the handler itself. -// -// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf -// and JSON codecs. They also support gzip compression. -func NewActivityServiceHandler(svc ActivityServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) { - activityServiceMethods := v1.File_api_v1_activity_service_proto.Services().ByName("ActivityService").Methods() - activityServiceListActivitiesHandler := connect.NewUnaryHandler( - ActivityServiceListActivitiesProcedure, - svc.ListActivities, - connect.WithSchema(activityServiceMethods.ByName("ListActivities")), - connect.WithHandlerOptions(opts...), - ) - activityServiceGetActivityHandler := connect.NewUnaryHandler( - ActivityServiceGetActivityProcedure, - svc.GetActivity, - connect.WithSchema(activityServiceMethods.ByName("GetActivity")), - connect.WithHandlerOptions(opts...), - ) - return "/memos.api.v1.ActivityService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case ActivityServiceListActivitiesProcedure: - activityServiceListActivitiesHandler.ServeHTTP(w, r) - case ActivityServiceGetActivityProcedure: - activityServiceGetActivityHandler.ServeHTTP(w, r) - default: - http.NotFound(w, r) - } - }) -} - -// UnimplementedActivityServiceHandler returns CodeUnimplemented from all methods. -type UnimplementedActivityServiceHandler struct{} - -func (UnimplementedActivityServiceHandler) ListActivities(context.Context, *connect.Request[v1.ListActivitiesRequest]) (*connect.Response[v1.ListActivitiesResponse], error) { - return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.ActivityService.ListActivities is not implemented")) -} - -func (UnimplementedActivityServiceHandler) GetActivity(context.Context, *connect.Request[v1.GetActivityRequest]) (*connect.Response[v1.Activity], error) { - return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.ActivityService.GetActivity is not implemented")) -} diff --git a/proto/gen/api/v1/user_service.pb.go b/proto/gen/api/v1/user_service.pb.go index 9f2d32e0d..bb210e8ab 100644 --- a/proto/gen/api/v1/user_service.pb.go +++ b/proto/gen/api/v1/user_service.pb.go @@ -2022,8 +2022,10 @@ type UserNotification struct { CreateTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty"` // The type of the notification. Type UserNotification_Type `protobuf:"varint,5,opt,name=type,proto3,enum=memos.api.v1.UserNotification_Type" json:"type,omitempty"` - // The activity ID associated with this notification. - ActivityId *int32 `protobuf:"varint,6,opt,name=activity_id,json=activityId,proto3,oneof" json:"activity_id,omitempty"` + // Types that are valid to be assigned to Payload: + // + // *UserNotification_MemoComment + Payload isUserNotification_Payload `protobuf_oneof:"payload"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -2093,13 +2095,32 @@ func (x *UserNotification) GetType() UserNotification_Type { return UserNotification_TYPE_UNSPECIFIED } -func (x *UserNotification) GetActivityId() int32 { - if x != nil && x.ActivityId != nil { - return *x.ActivityId +func (x *UserNotification) GetPayload() isUserNotification_Payload { + if x != nil { + return x.Payload } - return 0 + return nil } +func (x *UserNotification) GetMemoComment() *UserNotification_MemoCommentPayload { + if x != nil { + if x, ok := x.Payload.(*UserNotification_MemoComment); ok { + return x.MemoComment + } + } + return nil +} + +type isUserNotification_Payload interface { + isUserNotification_Payload() +} + +type UserNotification_MemoComment struct { + MemoComment *UserNotification_MemoCommentPayload `protobuf:"bytes,6,opt,name=memo_comment,json=memoComment,proto3,oneof"` +} + +func (*UserNotification_MemoComment) isUserNotification_Payload() {} + type ListUserNotificationsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The parent user resource. @@ -2500,6 +2521,62 @@ func (x *UserSetting_WebhooksSetting) GetWebhooks() []*UserWebhook { return nil } +type UserNotification_MemoCommentPayload struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The memo name of comment. + // Format: memos/{memo} + Memo string `protobuf:"bytes,1,opt,name=memo,proto3" json:"memo,omitempty"` + // The name of related memo. + // Format: memos/{memo} + RelatedMemo string `protobuf:"bytes,2,opt,name=related_memo,json=relatedMemo,proto3" json:"related_memo,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UserNotification_MemoCommentPayload) Reset() { + *x = UserNotification_MemoCommentPayload{} + mi := &file_api_v1_user_service_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UserNotification_MemoCommentPayload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UserNotification_MemoCommentPayload) ProtoMessage() {} + +func (x *UserNotification_MemoCommentPayload) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_user_service_proto_msgTypes[37] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UserNotification_MemoCommentPayload.ProtoReflect.Descriptor instead. +func (*UserNotification_MemoCommentPayload) Descriptor() ([]byte, []int) { + return file_api_v1_user_service_proto_rawDescGZIP(), []int{28, 0} +} + +func (x *UserNotification_MemoCommentPayload) GetMemo() string { + if x != nil { + return x.Memo + } + return "" +} + +func (x *UserNotification_MemoCommentPayload) GetRelatedMemo() string { + if x != nil { + return x.RelatedMemo + } + return "" +} + var File_api_v1_user_service_proto protoreflect.FileDescriptor const file_api_v1_user_service_proto_rawDesc = "" + @@ -2668,7 +2745,7 @@ const file_api_v1_user_service_proto_rawDesc = "" + "\vupdate_mask\x18\x02 \x01(\v2\x1a.google.protobuf.FieldMaskR\n" + "updateMask\"3\n" + "\x18DeleteUserWebhookRequest\x12\x17\n" + - "\x04name\x18\x01 \x01(\tB\x03\xe0A\x02R\x04name\"\xbe\x04\n" + + "\x04name\x18\x01 \x01(\tB\x03\xe0A\x02R\x04name\"\xb8\x05\n" + "\x10UserNotification\x12\x1a\n" + "\x04name\x18\x01 \x01(\tB\x06\xe0A\x03\xe0A\bR\x04name\x121\n" + "\x06sender\x18\x02 \x01(\tB\x19\xe0A\x03\xfaA\x13\n" + @@ -2676,9 +2753,11 @@ const file_api_v1_user_service_proto_rawDesc = "" + "\x06status\x18\x03 \x01(\x0e2%.memos.api.v1.UserNotification.StatusB\x03\xe0A\x01R\x06status\x12@\n" + "\vcreate_time\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampB\x03\xe0A\x03R\n" + "createTime\x12<\n" + - "\x04type\x18\x05 \x01(\x0e2#.memos.api.v1.UserNotification.TypeB\x03\xe0A\x03R\x04type\x12)\n" + - "\vactivity_id\x18\x06 \x01(\x05B\x03\xe0A\x01H\x00R\n" + - "activityId\x88\x01\x01\":\n" + + "\x04type\x18\x05 \x01(\x0e2#.memos.api.v1.UserNotification.TypeB\x03\xe0A\x03R\x04type\x12[\n" + + "\fmemo_comment\x18\x06 \x01(\v21.memos.api.v1.UserNotification.MemoCommentPayloadB\x03\xe0A\x03H\x00R\vmemoComment\x1aK\n" + + "\x12MemoCommentPayload\x12\x12\n" + + "\x04memo\x18\x01 \x01(\tR\x04memo\x12!\n" + + "\frelated_memo\x18\x02 \x01(\tR\vrelatedMemo\":\n" + "\x06Status\x12\x16\n" + "\x12STATUS_UNSPECIFIED\x10\x00\x12\n" + "\n" + @@ -2687,8 +2766,8 @@ const file_api_v1_user_service_proto_rawDesc = "" + "\x04Type\x12\x14\n" + "\x10TYPE_UNSPECIFIED\x10\x00\x12\x10\n" + "\fMEMO_COMMENT\x10\x01:p\xeaAm\n" + - "\x1dmemos.api.v1/UserNotification\x12)users/{user}/notifications/{notification}\x1a\x04name*\rnotifications2\fnotificationB\x0e\n" + - "\f_activity_id\"\xb4\x01\n" + + "\x1dmemos.api.v1/UserNotification\x12)users/{user}/notifications/{notification}\x1a\x04name*\rnotifications2\fnotificationB\t\n" + + "\apayload\"\xb4\x01\n" + "\x1cListUserNotificationsRequest\x121\n" + "\x06parent\x18\x01 \x01(\tB\x19\xe0A\x02\xfaA\x13\n" + "\x11memos.api.v1/UserR\x06parent\x12 \n" + @@ -2745,136 +2824,138 @@ func file_api_v1_user_service_proto_rawDescGZIP() []byte { } var file_api_v1_user_service_proto_enumTypes = make([]protoimpl.EnumInfo, 4) -var file_api_v1_user_service_proto_msgTypes = make([]protoimpl.MessageInfo, 37) +var file_api_v1_user_service_proto_msgTypes = make([]protoimpl.MessageInfo, 38) var file_api_v1_user_service_proto_goTypes = []any{ - (User_Role)(0), // 0: memos.api.v1.User.Role - (UserSetting_Key)(0), // 1: memos.api.v1.UserSetting.Key - (UserNotification_Status)(0), // 2: memos.api.v1.UserNotification.Status - (UserNotification_Type)(0), // 3: memos.api.v1.UserNotification.Type - (*User)(nil), // 4: memos.api.v1.User - (*ListUsersRequest)(nil), // 5: memos.api.v1.ListUsersRequest - (*ListUsersResponse)(nil), // 6: memos.api.v1.ListUsersResponse - (*GetUserRequest)(nil), // 7: memos.api.v1.GetUserRequest - (*CreateUserRequest)(nil), // 8: memos.api.v1.CreateUserRequest - (*UpdateUserRequest)(nil), // 9: memos.api.v1.UpdateUserRequest - (*DeleteUserRequest)(nil), // 10: memos.api.v1.DeleteUserRequest - (*UserStats)(nil), // 11: memos.api.v1.UserStats - (*GetUserStatsRequest)(nil), // 12: memos.api.v1.GetUserStatsRequest - (*ListAllUserStatsRequest)(nil), // 13: memos.api.v1.ListAllUserStatsRequest - (*ListAllUserStatsResponse)(nil), // 14: memos.api.v1.ListAllUserStatsResponse - (*UserSetting)(nil), // 15: memos.api.v1.UserSetting - (*GetUserSettingRequest)(nil), // 16: memos.api.v1.GetUserSettingRequest - (*UpdateUserSettingRequest)(nil), // 17: memos.api.v1.UpdateUserSettingRequest - (*ListUserSettingsRequest)(nil), // 18: memos.api.v1.ListUserSettingsRequest - (*ListUserSettingsResponse)(nil), // 19: memos.api.v1.ListUserSettingsResponse - (*PersonalAccessToken)(nil), // 20: memos.api.v1.PersonalAccessToken - (*ListPersonalAccessTokensRequest)(nil), // 21: memos.api.v1.ListPersonalAccessTokensRequest - (*ListPersonalAccessTokensResponse)(nil), // 22: memos.api.v1.ListPersonalAccessTokensResponse - (*CreatePersonalAccessTokenRequest)(nil), // 23: memos.api.v1.CreatePersonalAccessTokenRequest - (*CreatePersonalAccessTokenResponse)(nil), // 24: memos.api.v1.CreatePersonalAccessTokenResponse - (*DeletePersonalAccessTokenRequest)(nil), // 25: memos.api.v1.DeletePersonalAccessTokenRequest - (*UserWebhook)(nil), // 26: memos.api.v1.UserWebhook - (*ListUserWebhooksRequest)(nil), // 27: memos.api.v1.ListUserWebhooksRequest - (*ListUserWebhooksResponse)(nil), // 28: memos.api.v1.ListUserWebhooksResponse - (*CreateUserWebhookRequest)(nil), // 29: memos.api.v1.CreateUserWebhookRequest - (*UpdateUserWebhookRequest)(nil), // 30: memos.api.v1.UpdateUserWebhookRequest - (*DeleteUserWebhookRequest)(nil), // 31: memos.api.v1.DeleteUserWebhookRequest - (*UserNotification)(nil), // 32: memos.api.v1.UserNotification - (*ListUserNotificationsRequest)(nil), // 33: memos.api.v1.ListUserNotificationsRequest - (*ListUserNotificationsResponse)(nil), // 34: memos.api.v1.ListUserNotificationsResponse - (*UpdateUserNotificationRequest)(nil), // 35: memos.api.v1.UpdateUserNotificationRequest - (*DeleteUserNotificationRequest)(nil), // 36: memos.api.v1.DeleteUserNotificationRequest - nil, // 37: memos.api.v1.UserStats.TagCountEntry - (*UserStats_MemoTypeStats)(nil), // 38: memos.api.v1.UserStats.MemoTypeStats - (*UserSetting_GeneralSetting)(nil), // 39: memos.api.v1.UserSetting.GeneralSetting - (*UserSetting_WebhooksSetting)(nil), // 40: memos.api.v1.UserSetting.WebhooksSetting - (State)(0), // 41: memos.api.v1.State - (*timestamppb.Timestamp)(nil), // 42: google.protobuf.Timestamp - (*fieldmaskpb.FieldMask)(nil), // 43: google.protobuf.FieldMask - (*emptypb.Empty)(nil), // 44: google.protobuf.Empty + (User_Role)(0), // 0: memos.api.v1.User.Role + (UserSetting_Key)(0), // 1: memos.api.v1.UserSetting.Key + (UserNotification_Status)(0), // 2: memos.api.v1.UserNotification.Status + (UserNotification_Type)(0), // 3: memos.api.v1.UserNotification.Type + (*User)(nil), // 4: memos.api.v1.User + (*ListUsersRequest)(nil), // 5: memos.api.v1.ListUsersRequest + (*ListUsersResponse)(nil), // 6: memos.api.v1.ListUsersResponse + (*GetUserRequest)(nil), // 7: memos.api.v1.GetUserRequest + (*CreateUserRequest)(nil), // 8: memos.api.v1.CreateUserRequest + (*UpdateUserRequest)(nil), // 9: memos.api.v1.UpdateUserRequest + (*DeleteUserRequest)(nil), // 10: memos.api.v1.DeleteUserRequest + (*UserStats)(nil), // 11: memos.api.v1.UserStats + (*GetUserStatsRequest)(nil), // 12: memos.api.v1.GetUserStatsRequest + (*ListAllUserStatsRequest)(nil), // 13: memos.api.v1.ListAllUserStatsRequest + (*ListAllUserStatsResponse)(nil), // 14: memos.api.v1.ListAllUserStatsResponse + (*UserSetting)(nil), // 15: memos.api.v1.UserSetting + (*GetUserSettingRequest)(nil), // 16: memos.api.v1.GetUserSettingRequest + (*UpdateUserSettingRequest)(nil), // 17: memos.api.v1.UpdateUserSettingRequest + (*ListUserSettingsRequest)(nil), // 18: memos.api.v1.ListUserSettingsRequest + (*ListUserSettingsResponse)(nil), // 19: memos.api.v1.ListUserSettingsResponse + (*PersonalAccessToken)(nil), // 20: memos.api.v1.PersonalAccessToken + (*ListPersonalAccessTokensRequest)(nil), // 21: memos.api.v1.ListPersonalAccessTokensRequest + (*ListPersonalAccessTokensResponse)(nil), // 22: memos.api.v1.ListPersonalAccessTokensResponse + (*CreatePersonalAccessTokenRequest)(nil), // 23: memos.api.v1.CreatePersonalAccessTokenRequest + (*CreatePersonalAccessTokenResponse)(nil), // 24: memos.api.v1.CreatePersonalAccessTokenResponse + (*DeletePersonalAccessTokenRequest)(nil), // 25: memos.api.v1.DeletePersonalAccessTokenRequest + (*UserWebhook)(nil), // 26: memos.api.v1.UserWebhook + (*ListUserWebhooksRequest)(nil), // 27: memos.api.v1.ListUserWebhooksRequest + (*ListUserWebhooksResponse)(nil), // 28: memos.api.v1.ListUserWebhooksResponse + (*CreateUserWebhookRequest)(nil), // 29: memos.api.v1.CreateUserWebhookRequest + (*UpdateUserWebhookRequest)(nil), // 30: memos.api.v1.UpdateUserWebhookRequest + (*DeleteUserWebhookRequest)(nil), // 31: memos.api.v1.DeleteUserWebhookRequest + (*UserNotification)(nil), // 32: memos.api.v1.UserNotification + (*ListUserNotificationsRequest)(nil), // 33: memos.api.v1.ListUserNotificationsRequest + (*ListUserNotificationsResponse)(nil), // 34: memos.api.v1.ListUserNotificationsResponse + (*UpdateUserNotificationRequest)(nil), // 35: memos.api.v1.UpdateUserNotificationRequest + (*DeleteUserNotificationRequest)(nil), // 36: memos.api.v1.DeleteUserNotificationRequest + nil, // 37: memos.api.v1.UserStats.TagCountEntry + (*UserStats_MemoTypeStats)(nil), // 38: memos.api.v1.UserStats.MemoTypeStats + (*UserSetting_GeneralSetting)(nil), // 39: memos.api.v1.UserSetting.GeneralSetting + (*UserSetting_WebhooksSetting)(nil), // 40: memos.api.v1.UserSetting.WebhooksSetting + (*UserNotification_MemoCommentPayload)(nil), // 41: memos.api.v1.UserNotification.MemoCommentPayload + (State)(0), // 42: memos.api.v1.State + (*timestamppb.Timestamp)(nil), // 43: google.protobuf.Timestamp + (*fieldmaskpb.FieldMask)(nil), // 44: google.protobuf.FieldMask + (*emptypb.Empty)(nil), // 45: google.protobuf.Empty } var file_api_v1_user_service_proto_depIdxs = []int32{ 0, // 0: memos.api.v1.User.role:type_name -> memos.api.v1.User.Role - 41, // 1: memos.api.v1.User.state:type_name -> memos.api.v1.State - 42, // 2: memos.api.v1.User.create_time:type_name -> google.protobuf.Timestamp - 42, // 3: memos.api.v1.User.update_time:type_name -> google.protobuf.Timestamp + 42, // 1: memos.api.v1.User.state:type_name -> memos.api.v1.State + 43, // 2: memos.api.v1.User.create_time:type_name -> google.protobuf.Timestamp + 43, // 3: memos.api.v1.User.update_time:type_name -> google.protobuf.Timestamp 4, // 4: memos.api.v1.ListUsersResponse.users:type_name -> memos.api.v1.User - 43, // 5: memos.api.v1.GetUserRequest.read_mask:type_name -> google.protobuf.FieldMask + 44, // 5: memos.api.v1.GetUserRequest.read_mask:type_name -> google.protobuf.FieldMask 4, // 6: memos.api.v1.CreateUserRequest.user:type_name -> memos.api.v1.User 4, // 7: memos.api.v1.UpdateUserRequest.user:type_name -> memos.api.v1.User - 43, // 8: memos.api.v1.UpdateUserRequest.update_mask:type_name -> google.protobuf.FieldMask - 42, // 9: memos.api.v1.UserStats.memo_display_timestamps:type_name -> google.protobuf.Timestamp + 44, // 8: memos.api.v1.UpdateUserRequest.update_mask:type_name -> google.protobuf.FieldMask + 43, // 9: memos.api.v1.UserStats.memo_display_timestamps:type_name -> google.protobuf.Timestamp 38, // 10: memos.api.v1.UserStats.memo_type_stats:type_name -> memos.api.v1.UserStats.MemoTypeStats 37, // 11: memos.api.v1.UserStats.tag_count:type_name -> memos.api.v1.UserStats.TagCountEntry 11, // 12: memos.api.v1.ListAllUserStatsResponse.stats:type_name -> memos.api.v1.UserStats 39, // 13: memos.api.v1.UserSetting.general_setting:type_name -> memos.api.v1.UserSetting.GeneralSetting 40, // 14: memos.api.v1.UserSetting.webhooks_setting:type_name -> memos.api.v1.UserSetting.WebhooksSetting 15, // 15: memos.api.v1.UpdateUserSettingRequest.setting:type_name -> memos.api.v1.UserSetting - 43, // 16: memos.api.v1.UpdateUserSettingRequest.update_mask:type_name -> google.protobuf.FieldMask + 44, // 16: memos.api.v1.UpdateUserSettingRequest.update_mask:type_name -> google.protobuf.FieldMask 15, // 17: memos.api.v1.ListUserSettingsResponse.settings:type_name -> memos.api.v1.UserSetting - 42, // 18: memos.api.v1.PersonalAccessToken.created_at:type_name -> google.protobuf.Timestamp - 42, // 19: memos.api.v1.PersonalAccessToken.expires_at:type_name -> google.protobuf.Timestamp - 42, // 20: memos.api.v1.PersonalAccessToken.last_used_at:type_name -> google.protobuf.Timestamp + 43, // 18: memos.api.v1.PersonalAccessToken.created_at:type_name -> google.protobuf.Timestamp + 43, // 19: memos.api.v1.PersonalAccessToken.expires_at:type_name -> google.protobuf.Timestamp + 43, // 20: memos.api.v1.PersonalAccessToken.last_used_at:type_name -> google.protobuf.Timestamp 20, // 21: memos.api.v1.ListPersonalAccessTokensResponse.personal_access_tokens:type_name -> memos.api.v1.PersonalAccessToken 20, // 22: memos.api.v1.CreatePersonalAccessTokenResponse.personal_access_token:type_name -> memos.api.v1.PersonalAccessToken - 42, // 23: memos.api.v1.UserWebhook.create_time:type_name -> google.protobuf.Timestamp - 42, // 24: memos.api.v1.UserWebhook.update_time:type_name -> google.protobuf.Timestamp + 43, // 23: memos.api.v1.UserWebhook.create_time:type_name -> google.protobuf.Timestamp + 43, // 24: memos.api.v1.UserWebhook.update_time:type_name -> google.protobuf.Timestamp 26, // 25: memos.api.v1.ListUserWebhooksResponse.webhooks:type_name -> memos.api.v1.UserWebhook 26, // 26: memos.api.v1.CreateUserWebhookRequest.webhook:type_name -> memos.api.v1.UserWebhook 26, // 27: memos.api.v1.UpdateUserWebhookRequest.webhook:type_name -> memos.api.v1.UserWebhook - 43, // 28: memos.api.v1.UpdateUserWebhookRequest.update_mask:type_name -> google.protobuf.FieldMask + 44, // 28: memos.api.v1.UpdateUserWebhookRequest.update_mask:type_name -> google.protobuf.FieldMask 2, // 29: memos.api.v1.UserNotification.status:type_name -> memos.api.v1.UserNotification.Status - 42, // 30: memos.api.v1.UserNotification.create_time:type_name -> google.protobuf.Timestamp + 43, // 30: memos.api.v1.UserNotification.create_time:type_name -> google.protobuf.Timestamp 3, // 31: memos.api.v1.UserNotification.type:type_name -> memos.api.v1.UserNotification.Type - 32, // 32: memos.api.v1.ListUserNotificationsResponse.notifications:type_name -> memos.api.v1.UserNotification - 32, // 33: memos.api.v1.UpdateUserNotificationRequest.notification:type_name -> memos.api.v1.UserNotification - 43, // 34: memos.api.v1.UpdateUserNotificationRequest.update_mask:type_name -> google.protobuf.FieldMask - 26, // 35: memos.api.v1.UserSetting.WebhooksSetting.webhooks:type_name -> memos.api.v1.UserWebhook - 5, // 36: memos.api.v1.UserService.ListUsers:input_type -> memos.api.v1.ListUsersRequest - 7, // 37: memos.api.v1.UserService.GetUser:input_type -> memos.api.v1.GetUserRequest - 8, // 38: memos.api.v1.UserService.CreateUser:input_type -> memos.api.v1.CreateUserRequest - 9, // 39: memos.api.v1.UserService.UpdateUser:input_type -> memos.api.v1.UpdateUserRequest - 10, // 40: memos.api.v1.UserService.DeleteUser:input_type -> memos.api.v1.DeleteUserRequest - 13, // 41: memos.api.v1.UserService.ListAllUserStats:input_type -> memos.api.v1.ListAllUserStatsRequest - 12, // 42: memos.api.v1.UserService.GetUserStats:input_type -> memos.api.v1.GetUserStatsRequest - 16, // 43: memos.api.v1.UserService.GetUserSetting:input_type -> memos.api.v1.GetUserSettingRequest - 17, // 44: memos.api.v1.UserService.UpdateUserSetting:input_type -> memos.api.v1.UpdateUserSettingRequest - 18, // 45: memos.api.v1.UserService.ListUserSettings:input_type -> memos.api.v1.ListUserSettingsRequest - 21, // 46: memos.api.v1.UserService.ListPersonalAccessTokens:input_type -> memos.api.v1.ListPersonalAccessTokensRequest - 23, // 47: memos.api.v1.UserService.CreatePersonalAccessToken:input_type -> memos.api.v1.CreatePersonalAccessTokenRequest - 25, // 48: memos.api.v1.UserService.DeletePersonalAccessToken:input_type -> memos.api.v1.DeletePersonalAccessTokenRequest - 27, // 49: memos.api.v1.UserService.ListUserWebhooks:input_type -> memos.api.v1.ListUserWebhooksRequest - 29, // 50: memos.api.v1.UserService.CreateUserWebhook:input_type -> memos.api.v1.CreateUserWebhookRequest - 30, // 51: memos.api.v1.UserService.UpdateUserWebhook:input_type -> memos.api.v1.UpdateUserWebhookRequest - 31, // 52: memos.api.v1.UserService.DeleteUserWebhook:input_type -> memos.api.v1.DeleteUserWebhookRequest - 33, // 53: memos.api.v1.UserService.ListUserNotifications:input_type -> memos.api.v1.ListUserNotificationsRequest - 35, // 54: memos.api.v1.UserService.UpdateUserNotification:input_type -> memos.api.v1.UpdateUserNotificationRequest - 36, // 55: memos.api.v1.UserService.DeleteUserNotification:input_type -> memos.api.v1.DeleteUserNotificationRequest - 6, // 56: memos.api.v1.UserService.ListUsers:output_type -> memos.api.v1.ListUsersResponse - 4, // 57: memos.api.v1.UserService.GetUser:output_type -> memos.api.v1.User - 4, // 58: memos.api.v1.UserService.CreateUser:output_type -> memos.api.v1.User - 4, // 59: memos.api.v1.UserService.UpdateUser:output_type -> memos.api.v1.User - 44, // 60: memos.api.v1.UserService.DeleteUser:output_type -> google.protobuf.Empty - 14, // 61: memos.api.v1.UserService.ListAllUserStats:output_type -> memos.api.v1.ListAllUserStatsResponse - 11, // 62: memos.api.v1.UserService.GetUserStats:output_type -> memos.api.v1.UserStats - 15, // 63: memos.api.v1.UserService.GetUserSetting:output_type -> memos.api.v1.UserSetting - 15, // 64: memos.api.v1.UserService.UpdateUserSetting:output_type -> memos.api.v1.UserSetting - 19, // 65: memos.api.v1.UserService.ListUserSettings:output_type -> memos.api.v1.ListUserSettingsResponse - 22, // 66: memos.api.v1.UserService.ListPersonalAccessTokens:output_type -> memos.api.v1.ListPersonalAccessTokensResponse - 24, // 67: memos.api.v1.UserService.CreatePersonalAccessToken:output_type -> memos.api.v1.CreatePersonalAccessTokenResponse - 44, // 68: memos.api.v1.UserService.DeletePersonalAccessToken:output_type -> google.protobuf.Empty - 28, // 69: memos.api.v1.UserService.ListUserWebhooks:output_type -> memos.api.v1.ListUserWebhooksResponse - 26, // 70: memos.api.v1.UserService.CreateUserWebhook:output_type -> memos.api.v1.UserWebhook - 26, // 71: memos.api.v1.UserService.UpdateUserWebhook:output_type -> memos.api.v1.UserWebhook - 44, // 72: memos.api.v1.UserService.DeleteUserWebhook:output_type -> google.protobuf.Empty - 34, // 73: memos.api.v1.UserService.ListUserNotifications:output_type -> memos.api.v1.ListUserNotificationsResponse - 32, // 74: memos.api.v1.UserService.UpdateUserNotification:output_type -> memos.api.v1.UserNotification - 44, // 75: memos.api.v1.UserService.DeleteUserNotification:output_type -> google.protobuf.Empty - 56, // [56:76] is the sub-list for method output_type - 36, // [36:56] is the sub-list for method input_type - 36, // [36:36] is the sub-list for extension type_name - 36, // [36:36] is the sub-list for extension extendee - 0, // [0:36] is the sub-list for field type_name + 41, // 32: memos.api.v1.UserNotification.memo_comment:type_name -> memos.api.v1.UserNotification.MemoCommentPayload + 32, // 33: memos.api.v1.ListUserNotificationsResponse.notifications:type_name -> memos.api.v1.UserNotification + 32, // 34: memos.api.v1.UpdateUserNotificationRequest.notification:type_name -> memos.api.v1.UserNotification + 44, // 35: memos.api.v1.UpdateUserNotificationRequest.update_mask:type_name -> google.protobuf.FieldMask + 26, // 36: memos.api.v1.UserSetting.WebhooksSetting.webhooks:type_name -> memos.api.v1.UserWebhook + 5, // 37: memos.api.v1.UserService.ListUsers:input_type -> memos.api.v1.ListUsersRequest + 7, // 38: memos.api.v1.UserService.GetUser:input_type -> memos.api.v1.GetUserRequest + 8, // 39: memos.api.v1.UserService.CreateUser:input_type -> memos.api.v1.CreateUserRequest + 9, // 40: memos.api.v1.UserService.UpdateUser:input_type -> memos.api.v1.UpdateUserRequest + 10, // 41: memos.api.v1.UserService.DeleteUser:input_type -> memos.api.v1.DeleteUserRequest + 13, // 42: memos.api.v1.UserService.ListAllUserStats:input_type -> memos.api.v1.ListAllUserStatsRequest + 12, // 43: memos.api.v1.UserService.GetUserStats:input_type -> memos.api.v1.GetUserStatsRequest + 16, // 44: memos.api.v1.UserService.GetUserSetting:input_type -> memos.api.v1.GetUserSettingRequest + 17, // 45: memos.api.v1.UserService.UpdateUserSetting:input_type -> memos.api.v1.UpdateUserSettingRequest + 18, // 46: memos.api.v1.UserService.ListUserSettings:input_type -> memos.api.v1.ListUserSettingsRequest + 21, // 47: memos.api.v1.UserService.ListPersonalAccessTokens:input_type -> memos.api.v1.ListPersonalAccessTokensRequest + 23, // 48: memos.api.v1.UserService.CreatePersonalAccessToken:input_type -> memos.api.v1.CreatePersonalAccessTokenRequest + 25, // 49: memos.api.v1.UserService.DeletePersonalAccessToken:input_type -> memos.api.v1.DeletePersonalAccessTokenRequest + 27, // 50: memos.api.v1.UserService.ListUserWebhooks:input_type -> memos.api.v1.ListUserWebhooksRequest + 29, // 51: memos.api.v1.UserService.CreateUserWebhook:input_type -> memos.api.v1.CreateUserWebhookRequest + 30, // 52: memos.api.v1.UserService.UpdateUserWebhook:input_type -> memos.api.v1.UpdateUserWebhookRequest + 31, // 53: memos.api.v1.UserService.DeleteUserWebhook:input_type -> memos.api.v1.DeleteUserWebhookRequest + 33, // 54: memos.api.v1.UserService.ListUserNotifications:input_type -> memos.api.v1.ListUserNotificationsRequest + 35, // 55: memos.api.v1.UserService.UpdateUserNotification:input_type -> memos.api.v1.UpdateUserNotificationRequest + 36, // 56: memos.api.v1.UserService.DeleteUserNotification:input_type -> memos.api.v1.DeleteUserNotificationRequest + 6, // 57: memos.api.v1.UserService.ListUsers:output_type -> memos.api.v1.ListUsersResponse + 4, // 58: memos.api.v1.UserService.GetUser:output_type -> memos.api.v1.User + 4, // 59: memos.api.v1.UserService.CreateUser:output_type -> memos.api.v1.User + 4, // 60: memos.api.v1.UserService.UpdateUser:output_type -> memos.api.v1.User + 45, // 61: memos.api.v1.UserService.DeleteUser:output_type -> google.protobuf.Empty + 14, // 62: memos.api.v1.UserService.ListAllUserStats:output_type -> memos.api.v1.ListAllUserStatsResponse + 11, // 63: memos.api.v1.UserService.GetUserStats:output_type -> memos.api.v1.UserStats + 15, // 64: memos.api.v1.UserService.GetUserSetting:output_type -> memos.api.v1.UserSetting + 15, // 65: memos.api.v1.UserService.UpdateUserSetting:output_type -> memos.api.v1.UserSetting + 19, // 66: memos.api.v1.UserService.ListUserSettings:output_type -> memos.api.v1.ListUserSettingsResponse + 22, // 67: memos.api.v1.UserService.ListPersonalAccessTokens:output_type -> memos.api.v1.ListPersonalAccessTokensResponse + 24, // 68: memos.api.v1.UserService.CreatePersonalAccessToken:output_type -> memos.api.v1.CreatePersonalAccessTokenResponse + 45, // 69: memos.api.v1.UserService.DeletePersonalAccessToken:output_type -> google.protobuf.Empty + 28, // 70: memos.api.v1.UserService.ListUserWebhooks:output_type -> memos.api.v1.ListUserWebhooksResponse + 26, // 71: memos.api.v1.UserService.CreateUserWebhook:output_type -> memos.api.v1.UserWebhook + 26, // 72: memos.api.v1.UserService.UpdateUserWebhook:output_type -> memos.api.v1.UserWebhook + 45, // 73: memos.api.v1.UserService.DeleteUserWebhook:output_type -> google.protobuf.Empty + 34, // 74: memos.api.v1.UserService.ListUserNotifications:output_type -> memos.api.v1.ListUserNotificationsResponse + 32, // 75: memos.api.v1.UserService.UpdateUserNotification:output_type -> memos.api.v1.UserNotification + 45, // 76: memos.api.v1.UserService.DeleteUserNotification:output_type -> google.protobuf.Empty + 57, // [57:77] is the sub-list for method output_type + 37, // [37:57] is the sub-list for method input_type + 37, // [37:37] is the sub-list for extension type_name + 37, // [37:37] is the sub-list for extension extendee + 0, // [0:37] is the sub-list for field type_name } func init() { file_api_v1_user_service_proto_init() } @@ -2887,14 +2968,16 @@ func file_api_v1_user_service_proto_init() { (*UserSetting_GeneralSetting_)(nil), (*UserSetting_WebhooksSetting_)(nil), } - file_api_v1_user_service_proto_msgTypes[28].OneofWrappers = []any{} + file_api_v1_user_service_proto_msgTypes[28].OneofWrappers = []any{ + (*UserNotification_MemoComment)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_v1_user_service_proto_rawDesc), len(file_api_v1_user_service_proto_rawDesc)), NumEnums: 4, - NumMessages: 37, + NumMessages: 38, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/gen/openapi.yaml b/proto/gen/openapi.yaml index 52f291f01..1810fe22a 100644 --- a/proto/gen/openapi.yaml +++ b/proto/gen/openapi.yaml @@ -6,69 +6,6 @@ info: title: "" version: 0.0.1 paths: - /api/v1/activities: - get: - tags: - - ActivityService - description: ListActivities returns a list of activities. - operationId: ActivityService_ListActivities - parameters: - - name: pageSize - in: query - description: |- - The maximum number of activities to return. - The service may return fewer than this value. - If unspecified, at most 100 activities will be returned. - The maximum value is 1000; values above 1000 will be coerced to 1000. - schema: - type: integer - format: int32 - - name: pageToken - in: query - description: |- - A page token, received from a previous `ListActivities` call. - Provide this to retrieve the subsequent page. - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/ListActivitiesResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /api/v1/activities/{activity}: - get: - tags: - - ActivityService - description: GetActivity returns the activity with the given id. - operationId: ActivityService_GetActivity - parameters: - - name: activity - in: path - description: The activity id. - required: true - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/Activity' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' /api/v1/attachments: get: tags: @@ -1927,70 +1864,6 @@ paths: $ref: '#/components/schemas/Status' components: schemas: - Activity: - type: object - properties: - name: - readOnly: true - type: string - description: |- - The name of the activity. - Format: activities/{id} - creator: - readOnly: true - type: string - description: |- - The name of the creator. - Format: users/{user} - type: - readOnly: true - enum: - - TYPE_UNSPECIFIED - - MEMO_COMMENT - type: string - description: The type of the activity. - format: enum - level: - readOnly: true - enum: - - LEVEL_UNSPECIFIED - - INFO - - WARN - - ERROR - type: string - description: The level of the activity. - format: enum - createTime: - readOnly: true - type: string - description: The create time of the activity. - format: date-time - payload: - readOnly: true - allOf: - - $ref: '#/components/schemas/ActivityPayload' - description: The payload of the activity. - ActivityMemoCommentPayload: - type: object - properties: - memo: - type: string - description: |- - The memo name of comment. - Format: memos/{memo} - relatedMemo: - type: string - description: |- - The name of related memo. - Format: memos/{memo} - description: ActivityMemoCommentPayload represents the payload of a memo comment activity. - ActivityPayload: - type: object - properties: - memoComment: - allOf: - - $ref: '#/components/schemas/ActivityMemoCommentPayload' - description: Memo comment activity payload. Attachment: required: - filename @@ -2243,20 +2116,6 @@ components: - $ref: '#/components/schemas/StorageSetting_S3Config' description: The S3 config. description: Storage configuration settings for instance attachments. - ListActivitiesResponse: - type: object - properties: - activities: - type: array - items: - $ref: '#/components/schemas/Activity' - description: The activities. - nextPageToken: - type: string - description: |- - A token to retrieve the next page of results. - Pass this value in the page_token field in the subsequent call to `ListActivities` - method to retrieve the next page of results. ListAllUserStatsResponse: type: object properties: @@ -2942,10 +2801,23 @@ components: type: string description: The type of the notification. format: enum - activityId: - type: integer - description: The activity ID associated with this notification. - format: int32 + memoComment: + readOnly: true + allOf: + - $ref: '#/components/schemas/UserNotification_MemoCommentPayload' + UserNotification_MemoCommentPayload: + type: object + properties: + memo: + type: string + description: |- + The memo name of comment. + Format: memos/{memo} + relatedMemo: + type: string + description: |- + The name of related memo. + Format: memos/{memo} UserSetting: type: object properties: @@ -3061,7 +2933,6 @@ components: format: date-time description: UserWebhook represents a webhook owned by a user. tags: - - name: ActivityService - name: AttachmentService - name: AuthService - name: IdentityProviderService diff --git a/server/router/api/v1/acl_config_test.go b/server/router/api/v1/acl_config_test.go index 9a8d5a7a7..4316daf64 100644 --- a/server/router/api/v1/acl_config_test.go +++ b/server/router/api/v1/acl_config_test.go @@ -60,8 +60,6 @@ func TestProtectedMethodsRequireAuth(t *testing.T) { "/memos.api.v1.ShortcutService/ListShortcuts", "/memos.api.v1.ShortcutService/UpdateShortcut", "/memos.api.v1.ShortcutService/DeleteShortcut", - // Activity Service - "/memos.api.v1.ActivityService/GetActivity", } for _, method := range protectedMethods { diff --git a/server/router/api/v1/activity_service.go b/server/router/api/v1/activity_service.go deleted file mode 100644 index 33ac28d5d..000000000 --- a/server/router/api/v1/activity_service.go +++ /dev/null @@ -1,175 +0,0 @@ -package v1 - -import ( - "context" - "fmt" - "time" - - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/types/known/timestamppb" - - v1pb "github.com/usememos/memos/proto/gen/api/v1" - storepb "github.com/usememos/memos/proto/gen/store" - "github.com/usememos/memos/store" -) - -func (s *APIV1Service) ListActivities(ctx context.Context, request *v1pb.ListActivitiesRequest) (*v1pb.ListActivitiesResponse, error) { - var limit, offset int - if request.PageToken != "" { - var pageToken v1pb.PageToken - if err := unmarshalPageToken(request.PageToken, &pageToken); err != nil { - return nil, status.Errorf(codes.InvalidArgument, "invalid page token: %v", err) - } - limit = int(pageToken.Limit) - offset = int(pageToken.Offset) - } else { - limit = int(request.PageSize) - } - if limit <= 0 { - limit = DefaultPageSize - } - if limit > MaxPageSize { - limit = MaxPageSize - } - limitPlusOne := limit + 1 - activities, err := s.Store.ListActivities(ctx, &store.FindActivity{ - Limit: &limitPlusOne, - Offset: &offset, - }) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to list activities: %v", err) - } - - var activityMessages []*v1pb.Activity - nextPageToken := "" - if len(activities) == limitPlusOne { - activities = activities[:limit] - nextPageToken, err = getPageToken(limit, offset+limit) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to get next page token, error: %v", err) - } - } - - for _, activity := range activities { - activityMessage, err := s.convertActivityFromStore(ctx, activity) - if err != nil { - // Skip activities that reference deleted memos instead of failing the entire list - continue - } - if activityMessage != nil { - activityMessages = append(activityMessages, activityMessage) - } - } - - return &v1pb.ListActivitiesResponse{ - Activities: activityMessages, - NextPageToken: nextPageToken, - }, nil -} - -func (s *APIV1Service) GetActivity(ctx context.Context, request *v1pb.GetActivityRequest) (*v1pb.Activity, error) { - activityID, err := ExtractActivityIDFromName(request.Name) - if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "invalid activity name: %v", err) - } - activity, err := s.Store.GetActivity(ctx, &store.FindActivity{ - ID: &activityID, - }) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to get activity: %v", err) - } - - activityMessage, err := s.convertActivityFromStore(ctx, activity) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to convert activity from store: %v", err) - } - if activityMessage == nil { - return nil, status.Errorf(codes.NotFound, "activity references deleted content") - } - return activityMessage, nil -} - -// convertActivityFromStore converts a storage-layer activity to an API activity. -// This handles the mapping between internal activity representation and the public API, -// including proper type and level conversions. -// Returns nil if the activity references deleted content (to allow graceful skipping). -func (s *APIV1Service) convertActivityFromStore(ctx context.Context, activity *store.Activity) (*v1pb.Activity, error) { - payload, err := s.convertActivityPayloadFromStore(ctx, activity.Payload) - if err != nil { - return nil, err - } - // Skip activities that reference deleted memos - if payload == nil { - return nil, nil - } - - // Convert store activity type to proto enum - var activityType v1pb.Activity_Type - switch activity.Type { - case store.ActivityTypeMemoComment: - activityType = v1pb.Activity_MEMO_COMMENT - default: - activityType = v1pb.Activity_TYPE_UNSPECIFIED - } - - // Convert store activity level to proto enum - var activityLevel v1pb.Activity_Level - switch activity.Level { - case store.ActivityLevelInfo: - activityLevel = v1pb.Activity_INFO - default: - activityLevel = v1pb.Activity_LEVEL_UNSPECIFIED - } - - return &v1pb.Activity{ - Name: fmt.Sprintf("%s%d", ActivityNamePrefix, activity.ID), - Creator: fmt.Sprintf("%s%d", UserNamePrefix, activity.CreatorID), - Type: activityType, - Level: activityLevel, - CreateTime: timestamppb.New(time.Unix(activity.CreatedTs, 0)), - Payload: payload, - }, nil -} - -// convertActivityPayloadFromStore converts a storage-layer activity payload to an API payload. -// This resolves references (e.g., memo IDs) to resource names for the API. -// Returns nil if the activity references deleted content (to allow graceful skipping). -func (s *APIV1Service) convertActivityPayloadFromStore(ctx context.Context, payload *storepb.ActivityPayload) (*v1pb.ActivityPayload, error) { - v2Payload := &v1pb.ActivityPayload{} - if payload.MemoComment != nil { - // Fetch the comment memo - memo, err := s.Store.GetMemo(ctx, &store.FindMemo{ - ID: &payload.MemoComment.MemoId, - ExcludeContent: true, - }) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to get memo: %v", err) - } - // If the comment memo was deleted, skip this activity gracefully - if memo == nil { - return nil, nil - } - - // Fetch the related memo (the one being commented on) - relatedMemo, err := s.Store.GetMemo(ctx, &store.FindMemo{ - ID: &payload.MemoComment.RelatedMemoId, - ExcludeContent: true, - }) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to get related memo: %v", err) - } - // If the related memo was deleted, skip this activity gracefully - if relatedMemo == nil { - return nil, nil - } - - v2Payload.Payload = &v1pb.ActivityPayload_MemoComment{ - MemoComment: &v1pb.ActivityMemoCommentPayload{ - Memo: fmt.Sprintf("%s%s", MemoNamePrefix, memo.UID), - RelatedMemo: fmt.Sprintf("%s%s", MemoNamePrefix, relatedMemo.UID), - }, - } - } - return v2Payload, nil -} diff --git a/server/router/api/v1/connect_handler.go b/server/router/api/v1/connect_handler.go index b1cca529d..349b8fdc9 100644 --- a/server/router/api/v1/connect_handler.go +++ b/server/router/api/v1/connect_handler.go @@ -40,7 +40,6 @@ func (s *ConnectServiceHandler) RegisterConnectHandlers(mux *http.ServeMux, opts wrap(apiv1connect.NewMemoServiceHandler(s, opts...)), wrap(apiv1connect.NewAttachmentServiceHandler(s, opts...)), wrap(apiv1connect.NewShortcutServiceHandler(s, opts...)), - wrap(apiv1connect.NewActivityServiceHandler(s, opts...)), wrap(apiv1connect.NewIdentityProviderServiceHandler(s, opts...)), } diff --git a/server/router/api/v1/connect_services.go b/server/router/api/v1/connect_services.go index d8124d8aa..ecc7f625f 100644 --- a/server/router/api/v1/connect_services.go +++ b/server/router/api/v1/connect_services.go @@ -429,24 +429,6 @@ func (s *ConnectServiceHandler) DeleteShortcut(ctx context.Context, req *connect return connect.NewResponse(resp), nil } -// ActivityService - -func (s *ConnectServiceHandler) ListActivities(ctx context.Context, req *connect.Request[v1pb.ListActivitiesRequest]) (*connect.Response[v1pb.ListActivitiesResponse], error) { - resp, err := s.APIV1Service.ListActivities(ctx, req.Msg) - if err != nil { - return nil, convertGRPCError(err) - } - return connect.NewResponse(resp), nil -} - -func (s *ConnectServiceHandler) GetActivity(ctx context.Context, req *connect.Request[v1pb.GetActivityRequest]) (*connect.Response[v1pb.Activity], error) { - resp, err := s.APIV1Service.GetActivity(ctx, req.Msg) - if err != nil { - return nil, convertGRPCError(err) - } - return connect.NewResponse(resp), nil -} - // IdentityProviderService func (s *ConnectServiceHandler) ListIdentityProviders(ctx context.Context, req *connect.Request[v1pb.ListIdentityProvidersRequest]) (*connect.Response[v1pb.ListIdentityProvidersResponse], error) { diff --git a/server/router/api/v1/resource_name.go b/server/router/api/v1/resource_name.go index d201416e1..f7b74d42a 100644 --- a/server/router/api/v1/resource_name.go +++ b/server/router/api/v1/resource_name.go @@ -21,7 +21,6 @@ const ( ReactionNamePrefix = "reactions/" InboxNamePrefix = "inboxes/" IdentityProviderNamePrefix = "identity-providers/" - ActivityNamePrefix = "activities/" WebhookNamePrefix = "webhooks/" ) @@ -145,18 +144,6 @@ func ExtractIdentityProviderUIDFromName(name string) (string, error) { return tokens[0], nil } -func ExtractActivityIDFromName(name string) (int32, error) { - tokens, err := GetNameParentTokens(name, ActivityNamePrefix) - if err != nil { - return 0, err - } - id, err := util.ConvertStringToInt32(tokens[0]) - if err != nil { - return 0, errors.Errorf("invalid activity ID %q", tokens[0]) - } - return id, nil -} - // ValidateAndGenerateUID validates a user-provided UID or generates a new one. // If provided is empty, a new shortuuid is generated. // If provided is non-empty, it is validated against base.UIDMatcher. diff --git a/server/router/api/v1/test/activity_deleted_memo_test.go b/server/router/api/v1/test/activity_deleted_memo_test.go deleted file mode 100644 index 33d2206ed..000000000 --- a/server/router/api/v1/test/activity_deleted_memo_test.go +++ /dev/null @@ -1,263 +0,0 @@ -package test - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" - - apiv1 "github.com/usememos/memos/proto/gen/api/v1" - v1 "github.com/usememos/memos/server/router/api/v1" //nolint:revive - "github.com/usememos/memos/store" -) - -// TestListActivitiesWithDeletedMemos verifies that ListActivities gracefully handles -// activities that reference deleted memos instead of crashing the entire request. -func TestListActivitiesWithDeletedMemos(t *testing.T) { - ctx := context.Background() - ts := NewTestService(t) - defer ts.Cleanup() - - // Create two users - one to create memo, one to comment - userOne, err := ts.CreateRegularUser(ctx, "test-user-1") - require.NoError(t, err) - userOneCtx := ts.CreateUserContext(ctx, userOne.ID) - - userTwo, err := ts.CreateRegularUser(ctx, "test-user-2") - require.NoError(t, err) - userTwoCtx := ts.CreateUserContext(ctx, userTwo.ID) - - // Create a memo by userOne - memo1, err := ts.Service.CreateMemo(userOneCtx, &apiv1.CreateMemoRequest{ - Memo: &apiv1.Memo{ - Content: "Original memo", - Visibility: apiv1.Visibility_PUBLIC, - }, - }) - require.NoError(t, err) - require.NotNil(t, memo1) - - // Create a comment on the memo by userTwo (this will create an activity for userOne) - comment, err := ts.Service.CreateMemoComment(userTwoCtx, &apiv1.CreateMemoCommentRequest{ - Name: memo1.Name, - Comment: &apiv1.Memo{ - Content: "This is a comment", - Visibility: apiv1.Visibility_PUBLIC, - }, - }) - require.NoError(t, err) - require.NotNil(t, comment) - - // Verify activity was created for the comment (check from userOne's perspective - they receive the notification) - activities, err := ts.Service.ListActivities(userOneCtx, &apiv1.ListActivitiesRequest{}) - require.NoError(t, err) - initialActivityCount := len(activities.Activities) - require.Greater(t, initialActivityCount, 0, "Should have at least one activity") - - // Delete the original memo (this deletes the comment too) - _, err = ts.Service.DeleteMemo(userOneCtx, &apiv1.DeleteMemoRequest{ - Name: memo1.Name, - }) - require.NoError(t, err) - - // List activities again - should succeed even though the memo is deleted - activities, err = ts.Service.ListActivities(userOneCtx, &apiv1.ListActivitiesRequest{}) - require.NoError(t, err) - // Activities list should be empty or not contain the deleted memo activity - for _, activity := range activities.Activities { - if activity.Payload != nil && activity.Payload.GetMemoComment() != nil { - require.NotEqual(t, memo1.Name, activity.Payload.GetMemoComment().Memo, - "Activity should not reference deleted memo") - } - } - // After deletion, there should be fewer activities - require.LessOrEqual(t, len(activities.Activities), initialActivityCount-1, - "Should have filtered out the activity for the deleted memo") -} - -// TestGetActivityWithDeletedMemo verifies that GetActivity returns a proper error -// when trying to fetch an activity that references a deleted memo. -func TestGetActivityWithDeletedMemo(t *testing.T) { - ctx := context.Background() - ts := NewTestService(t) - defer ts.Cleanup() - - // Create two users - userOne, err := ts.CreateRegularUser(ctx, "test-user-1") - require.NoError(t, err) - userOneCtx := ts.CreateUserContext(ctx, userOne.ID) - - userTwo, err := ts.CreateRegularUser(ctx, "test-user-2") - require.NoError(t, err) - userTwoCtx := ts.CreateUserContext(ctx, userTwo.ID) - - // Create a memo by userOne - memo1, err := ts.Service.CreateMemo(userOneCtx, &apiv1.CreateMemoRequest{ - Memo: &apiv1.Memo{ - Content: "Original memo", - Visibility: apiv1.Visibility_PUBLIC, - }, - }) - require.NoError(t, err) - require.NotNil(t, memo1) - - // Create a comment to trigger activity creation by userTwo - comment, err := ts.Service.CreateMemoComment(userTwoCtx, &apiv1.CreateMemoCommentRequest{ - Name: memo1.Name, - Comment: &apiv1.Memo{ - Content: "Comment", - Visibility: apiv1.Visibility_PUBLIC, - }, - }) - require.NoError(t, err) - require.NotNil(t, comment) - - // Get the activity ID by listing activities from userOne's perspective - activities, err := ts.Service.ListActivities(userOneCtx, &apiv1.ListActivitiesRequest{}) - require.NoError(t, err) - require.Greater(t, len(activities.Activities), 0) - - activityName := activities.Activities[0].Name - - // Delete the memo - _, err = ts.Service.DeleteMemo(userOneCtx, &apiv1.DeleteMemoRequest{ - Name: memo1.Name, - }) - require.NoError(t, err) - - // Try to get the specific activity - should return NotFound error - _, err = ts.Service.GetActivity(userOneCtx, &apiv1.GetActivityRequest{ - Name: activityName, - }) - require.Error(t, err) - require.Contains(t, err.Error(), "activity references deleted content") -} - -// TestActivitiesWithPartiallyDeletedMemos verifies that when some memos are deleted, -// other valid activities are still returned. -func TestActivitiesWithPartiallyDeletedMemos(t *testing.T) { - ctx := context.Background() - ts := NewTestService(t) - defer ts.Cleanup() - - // Create two users - userOne, err := ts.CreateRegularUser(ctx, "test-user-1") - require.NoError(t, err) - userOneCtx := ts.CreateUserContext(ctx, userOne.ID) - - userTwo, err := ts.CreateRegularUser(ctx, "test-user-2") - require.NoError(t, err) - userTwoCtx := ts.CreateUserContext(ctx, userTwo.ID) - - // Create two memos by userOne - memo1, err := ts.Service.CreateMemo(userOneCtx, &apiv1.CreateMemoRequest{ - Memo: &apiv1.Memo{ - Content: "First memo", - Visibility: apiv1.Visibility_PUBLIC, - }, - }) - require.NoError(t, err) - - memo2, err := ts.Service.CreateMemo(userOneCtx, &apiv1.CreateMemoRequest{ - Memo: &apiv1.Memo{ - Content: "Second memo", - Visibility: apiv1.Visibility_PUBLIC, - }, - }) - require.NoError(t, err) - - // Create comments on both by userTwo (creates activities for userOne) - _, err = ts.Service.CreateMemoComment(userTwoCtx, &apiv1.CreateMemoCommentRequest{ - Name: memo1.Name, - Comment: &apiv1.Memo{ - Content: "Comment on first", - Visibility: apiv1.Visibility_PUBLIC, - }, - }) - require.NoError(t, err) - - _, err = ts.Service.CreateMemoComment(userTwoCtx, &apiv1.CreateMemoCommentRequest{ - Name: memo2.Name, - Comment: &apiv1.Memo{ - Content: "Comment on second", - Visibility: apiv1.Visibility_PUBLIC, - }, - }) - require.NoError(t, err) - - // Should have 2 activities from userOne's perspective - activities, err := ts.Service.ListActivities(userOneCtx, &apiv1.ListActivitiesRequest{}) - require.NoError(t, err) - require.Equal(t, 2, len(activities.Activities)) - - // Delete first memo - _, err = ts.Service.DeleteMemo(userOneCtx, &apiv1.DeleteMemoRequest{ - Name: memo1.Name, - }) - require.NoError(t, err) - - // List activities - should still work and return only the second memo's activity - activities, err = ts.Service.ListActivities(userOneCtx, &apiv1.ListActivitiesRequest{}) - require.NoError(t, err) - require.Equal(t, 1, len(activities.Activities), "Should have 1 activity remaining") - - // Verify the remaining activity relates to a valid memo - require.NotNil(t, activities.Activities[0].Payload.GetMemoComment()) - require.Contains(t, activities.Activities[0].Payload.GetMemoComment().RelatedMemo, "memos/") -} - -// TestActivityStoreDirectDeletion tests the scenario where a memo is deleted directly -// from the store (simulating database-level deletion or migration). -func TestActivityStoreDirectDeletion(t *testing.T) { - ctx := context.Background() - ts := NewTestService(t) - defer ts.Cleanup() - - user, err := ts.CreateRegularUser(ctx, "test-user") - require.NoError(t, err) - userCtx := ts.CreateUserContext(ctx, user.ID) - - // Create a memo - memo1, err := ts.Service.CreateMemo(userCtx, &apiv1.CreateMemoRequest{ - Memo: &apiv1.Memo{ - Content: "Test memo", - Visibility: apiv1.Visibility_PUBLIC, - }, - }) - require.NoError(t, err) - - // Create a comment - comment, err := ts.Service.CreateMemoComment(userCtx, &apiv1.CreateMemoCommentRequest{ - Name: memo1.Name, - Comment: &apiv1.Memo{ - Content: "Test comment", - Visibility: apiv1.Visibility_PUBLIC, - }, - }) - require.NoError(t, err) - - // Extract memo UID from the comment name - commentMemoUID, err := v1.ExtractMemoUIDFromName(comment.Name) - require.NoError(t, err) - - commentMemo, err := ts.Store.GetMemo(ctx, &store.FindMemo{ - UID: &commentMemoUID, - }) - require.NoError(t, err) - require.NotNil(t, commentMemo) - - // Delete the comment memo directly from store (simulating orphaned activity) - err = ts.Store.DeleteMemo(ctx, &store.DeleteMemo{ID: commentMemo.ID}) - require.NoError(t, err) - - // List activities should still succeed even with orphaned activity - activities, err := ts.Service.ListActivities(userCtx, &apiv1.ListActivitiesRequest{}) - require.NoError(t, err) - // Activities should be empty or not include the orphaned one - for _, activity := range activities.Activities { - if activity.Payload != nil && activity.Payload.GetMemoComment() != nil { - require.NotEqual(t, comment.Name, activity.Payload.GetMemoComment().Memo, - "Should not return activity with deleted memo") - } - } -} diff --git a/server/router/api/v1/test/activity_service_test.go b/server/router/api/v1/test/activity_service_test.go deleted file mode 100644 index bb1e9e7f3..000000000 --- a/server/router/api/v1/test/activity_service_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package test - -import ( - "context" - "fmt" - "testing" - - "github.com/stretchr/testify/require" - - apiv1 "github.com/usememos/memos/proto/gen/api/v1" -) - -func TestListActivities(t *testing.T) { - ctx := context.Background() - - ts := NewTestService(t) - defer ts.Cleanup() - - // Create userOne - userOne, err := ts.CreateRegularUser(ctx, "test-user-1") - require.NoError(t, err) - userOneCtx := ts.CreateUserContext(ctx, userOne.ID) - - // Create userTwo - userTwo, err := ts.CreateRegularUser(ctx, "test-user-2") - require.NoError(t, err) - userTwoCtx := ts.CreateUserContext(ctx, userTwo.ID) - - // UserOne creates a memo - memo, err := ts.Service.CreateMemo(userOneCtx, &apiv1.CreateMemoRequest{ - Memo: &apiv1.Memo{ - Content: "Base memo", - Visibility: apiv1.Visibility_PUBLIC, - }, - }) - require.NoError(t, err) - - // UserTwo creates 15 comments on the memo to generate 15 activities - for i := 0; i < 15; i++ { - _, err := ts.Service.CreateMemoComment(userTwoCtx, &apiv1.CreateMemoCommentRequest{ - Name: memo.Name, - Comment: &apiv1.Memo{ - Content: fmt.Sprintf("Comment %d", i), - Visibility: apiv1.Visibility_PUBLIC, - }, - }) - require.NoError(t, err) - } - - // List activities with page size 10 (as admin or userOne) - // Activities are visible to the receiver (UserOne) - resp, err := ts.Service.ListActivities(userOneCtx, &apiv1.ListActivitiesRequest{ - PageSize: 10, - }) - require.NoError(t, err) - require.Len(t, resp.Activities, 10) - require.NotEmpty(t, resp.NextPageToken) - - // List next page - resp, err = ts.Service.ListActivities(userOneCtx, &apiv1.ListActivitiesRequest{ - PageSize: 10, - PageToken: resp.NextPageToken, - }) - require.NoError(t, err) - require.Len(t, resp.Activities, 5) - require.Empty(t, resp.NextPageToken) -} diff --git a/server/router/api/v1/test/user_notification_test.go b/server/router/api/v1/test/user_notification_test.go new file mode 100644 index 000000000..f1792c5d8 --- /dev/null +++ b/server/router/api/v1/test/user_notification_test.go @@ -0,0 +1,151 @@ +package test + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + apiv1 "github.com/usememos/memos/proto/gen/api/v1" + storepb "github.com/usememos/memos/proto/gen/store" + "github.com/usememos/memos/store" +) + +func TestListUserNotificationsIncludesMemoCommentPayload(t *testing.T) { + ctx := context.Background() + ts := NewTestService(t) + defer ts.Cleanup() + + owner, err := ts.CreateRegularUser(ctx, "notification-owner") + require.NoError(t, err) + ownerCtx := ts.CreateUserContext(ctx, owner.ID) + + commenter, err := ts.CreateRegularUser(ctx, "notification-commenter") + require.NoError(t, err) + commenterCtx := ts.CreateUserContext(ctx, commenter.ID) + + memo, err := ts.Service.CreateMemo(ownerCtx, &apiv1.CreateMemoRequest{ + Memo: &apiv1.Memo{ + Content: "Base memo", + Visibility: apiv1.Visibility_PUBLIC, + }, + }) + require.NoError(t, err) + + comment, err := ts.Service.CreateMemoComment(commenterCtx, &apiv1.CreateMemoCommentRequest{ + Name: memo.Name, + Comment: &apiv1.Memo{ + Content: "Comment content", + Visibility: apiv1.Visibility_PUBLIC, + }, + }) + require.NoError(t, err) + + resp, err := ts.Service.ListUserNotifications(ownerCtx, &apiv1.ListUserNotificationsRequest{ + Parent: fmt.Sprintf("users/%d", owner.ID), + }) + require.NoError(t, err) + require.Len(t, resp.Notifications, 1) + + notification := resp.Notifications[0] + require.Equal(t, apiv1.UserNotification_MEMO_COMMENT, notification.Type) + require.NotNil(t, notification.GetMemoComment()) + require.Equal(t, comment.Name, notification.GetMemoComment().Memo) + require.Equal(t, memo.Name, notification.GetMemoComment().RelatedMemo) +} + +func TestListUserNotificationsOmitsPayloadWhenActivityMissing(t *testing.T) { + ctx := context.Background() + ts := NewTestService(t) + defer ts.Cleanup() + + owner, err := ts.CreateRegularUser(ctx, "notification-owner") + require.NoError(t, err) + ownerCtx := ts.CreateUserContext(ctx, owner.ID) + + commenter, err := ts.CreateRegularUser(ctx, "notification-commenter") + require.NoError(t, err) + commenterCtx := ts.CreateUserContext(ctx, commenter.ID) + + memo, err := ts.Service.CreateMemo(ownerCtx, &apiv1.CreateMemoRequest{ + Memo: &apiv1.Memo{ + Content: "Base memo", + Visibility: apiv1.Visibility_PUBLIC, + }, + }) + require.NoError(t, err) + + _, err = ts.Service.CreateMemoComment(commenterCtx, &apiv1.CreateMemoCommentRequest{ + Name: memo.Name, + Comment: &apiv1.Memo{ + Content: "Comment content", + Visibility: apiv1.Visibility_PUBLIC, + }, + }) + require.NoError(t, err) + + messageType := storepb.InboxMessage_MEMO_COMMENT + inboxes, err := ts.Store.ListInboxes(ctx, &store.FindInbox{ + ReceiverID: &owner.ID, + MessageType: &messageType, + }) + require.NoError(t, err) + require.Len(t, inboxes, 1) + require.NotNil(t, inboxes[0].Message) + require.NotNil(t, inboxes[0].Message.ActivityId) + + _, err = ts.Store.GetDriver().GetDB().ExecContext(ctx, "DELETE FROM activity WHERE id = ?", *inboxes[0].Message.ActivityId) + require.NoError(t, err) + + resp, err := ts.Service.ListUserNotifications(ownerCtx, &apiv1.ListUserNotificationsRequest{ + Parent: fmt.Sprintf("users/%d", owner.ID), + }) + require.NoError(t, err) + require.Len(t, resp.Notifications, 1) + require.Nil(t, resp.Notifications[0].GetMemoComment()) +} + +func TestListUserNotificationsOmitsPayloadWhenMemosDeleted(t *testing.T) { + ctx := context.Background() + ts := NewTestService(t) + defer ts.Cleanup() + + owner, err := ts.CreateRegularUser(ctx, "notification-owner") + require.NoError(t, err) + ownerCtx := ts.CreateUserContext(ctx, owner.ID) + + commenter, err := ts.CreateRegularUser(ctx, "notification-commenter") + require.NoError(t, err) + commenterCtx := ts.CreateUserContext(ctx, commenter.ID) + + memo, err := ts.Service.CreateMemo(ownerCtx, &apiv1.CreateMemoRequest{ + Memo: &apiv1.Memo{ + Content: "Base memo", + Visibility: apiv1.Visibility_PUBLIC, + }, + }) + require.NoError(t, err) + + _, err = ts.Service.CreateMemoComment(commenterCtx, &apiv1.CreateMemoCommentRequest{ + Name: memo.Name, + Comment: &apiv1.Memo{ + Content: "Comment content", + Visibility: apiv1.Visibility_PUBLIC, + }, + }) + require.NoError(t, err) + + _, err = ts.Service.DeleteMemo(ownerCtx, &apiv1.DeleteMemoRequest{ + Name: memo.Name, + }) + require.NoError(t, err) + + resp, err := ts.Service.ListUserNotifications(ownerCtx, &apiv1.ListUserNotificationsRequest{ + Parent: fmt.Sprintf("users/%d", owner.ID), + }) + require.NoError(t, err) + require.Len(t, resp.Notifications, 1) + require.Equal(t, apiv1.UserNotification_MEMO_COMMENT, resp.Notifications[0].Type) + require.Nil(t, resp.Notifications[0].GetMemoComment()) +} diff --git a/server/router/api/v1/user_service.go b/server/router/api/v1/user_service.go index e718a8b15..cb0649707 100644 --- a/server/router/api/v1/user_service.go +++ b/server/router/api/v1/user_service.go @@ -1399,7 +1399,7 @@ func (s *APIV1Service) DeleteUserNotification(ctx context.Context, request *v1pb // convertInboxToUserNotification converts a storage-layer inbox to an API notification. // This handles the mapping between the internal inbox representation and the public API. -func (*APIV1Service) convertInboxToUserNotification(_ context.Context, inbox *store.Inbox) (*v1pb.UserNotification, error) { +func (s *APIV1Service) convertInboxToUserNotification(ctx context.Context, inbox *store.Inbox) (*v1pb.UserNotification, error) { notification := &v1pb.UserNotification{ Name: fmt.Sprintf("users/%d/notifications/%d", inbox.ReceiverID, inbox.ID), Sender: fmt.Sprintf("%s%d", UserNamePrefix, inbox.SenderID), @@ -1425,14 +1425,63 @@ func (*APIV1Service) convertInboxToUserNotification(_ context.Context, inbox *st notification.Type = v1pb.UserNotification_TYPE_UNSPECIFIED } - if inbox.Message.ActivityId != nil { - notification.ActivityId = inbox.Message.ActivityId + payload, err := s.convertUserNotificationPayload(ctx, inbox.Message) + if err != nil { + return nil, err + } + if payload != nil { + notification.Payload = &v1pb.UserNotification_MemoComment{ + MemoComment: payload, + } } } return notification, nil } +func (s *APIV1Service) convertUserNotificationPayload(ctx context.Context, message *storepb.InboxMessage) (*v1pb.UserNotification_MemoCommentPayload, error) { + if message == nil || message.Type != storepb.InboxMessage_MEMO_COMMENT || message.ActivityId == nil { + return nil, nil + } + + activity, err := s.Store.GetActivity(ctx, &store.FindActivity{ + ID: message.ActivityId, + }) + if err != nil { + return nil, errors.Wrap(err, "failed to get activity") + } + if activity == nil || activity.Payload == nil || activity.Payload.MemoComment == nil { + return nil, nil + } + + commentMemo, err := s.Store.GetMemo(ctx, &store.FindMemo{ + ID: &activity.Payload.MemoComment.MemoId, + ExcludeContent: true, + }) + if err != nil { + return nil, errors.Wrap(err, "failed to get comment memo") + } + if commentMemo == nil { + return nil, nil + } + + relatedMemo, err := s.Store.GetMemo(ctx, &store.FindMemo{ + ID: &activity.Payload.MemoComment.RelatedMemoId, + ExcludeContent: true, + }) + if err != nil { + return nil, errors.Wrap(err, "failed to get related memo") + } + if relatedMemo == nil { + return nil, nil + } + + return &v1pb.UserNotification_MemoCommentPayload{ + Memo: fmt.Sprintf("%s%s", MemoNamePrefix, commentMemo.UID), + RelatedMemo: fmt.Sprintf("%s%s", MemoNamePrefix, relatedMemo.UID), + }, nil +} + // ExtractNotificationIDFromName extracts the notification ID from a resource name. // Expected format: users/{user_id}/notifications/{notification_id}. func ExtractNotificationIDFromName(name string) (int32, error) { diff --git a/server/router/api/v1/v1.go b/server/router/api/v1/v1.go index 79c140f07..4d5e5c329 100644 --- a/server/router/api/v1/v1.go +++ b/server/router/api/v1/v1.go @@ -24,7 +24,6 @@ type APIV1Service struct { v1pb.UnimplementedMemoServiceServer v1pb.UnimplementedAttachmentServiceServer v1pb.UnimplementedShortcutServiceServer - v1pb.UnimplementedActivityServiceServer v1pb.UnimplementedIdentityProviderServiceServer Secret string @@ -107,9 +106,6 @@ func (s *APIV1Service) RegisterGateway(ctx context.Context, echoServer *echo.Ech if err := v1pb.RegisterShortcutServiceHandlerServer(ctx, gwMux, s); err != nil { return err } - if err := v1pb.RegisterActivityServiceHandlerServer(ctx, gwMux, s); err != nil { - return err - } if err := v1pb.RegisterIdentityProviderServiceHandlerServer(ctx, gwMux, s); err != nil { return err } diff --git a/web/src/components/Inbox/MemoCommentMessage.tsx b/web/src/components/Inbox/MemoCommentMessage.tsx index f78cf0277..48c67ea43 100644 --- a/web/src/components/Inbox/MemoCommentMessage.tsx +++ b/web/src/components/Inbox/MemoCommentMessage.tsx @@ -4,8 +4,7 @@ import { CheckIcon, MessageCircleIcon, TrashIcon, XIcon } from "lucide-react"; import { useState } from "react"; import toast from "react-hot-toast"; import UserAvatar from "@/components/UserAvatar"; -import { activityServiceClient, memoServiceClient, userServiceClient } from "@/connect"; -import { activityNamePrefix } from "@/helpers/resource-names"; +import { memoServiceClient, userServiceClient } from "@/connect"; import useAsyncEffect from "@/hooks/useAsyncEffect"; import useNavigateTo from "@/hooks/useNavigateTo"; import { useUser } from "@/hooks/useUserQueries"; @@ -31,38 +30,33 @@ function MemoCommentMessage({ notification }: Props) { const { data: sender } = useUser(senderName || "", { enabled: !!senderName }); useAsyncEffect(async () => { - if (!notification.activityId) { + if (notification.payload?.case !== "memoComment") { + setHasError(true); return; } try { - const activity = await activityServiceClient.getActivity({ - name: `${activityNamePrefix}${notification.activityId}`, + const memoCommentPayload = notification.payload.value; + const memo = await memoServiceClient.getMemo({ + name: memoCommentPayload.relatedMemo, }); + setRelatedMemo(memo); - if (activity.payload?.payload?.case === "memoComment") { - const memoCommentPayload = activity.payload.payload.value; - const memo = await memoServiceClient.getMemo({ - name: memoCommentPayload.relatedMemo, - }); - setRelatedMemo(memo); + const comment = await memoServiceClient.getMemo({ + name: memoCommentPayload.memo, + }); + setCommentMemo(comment); - const comment = await memoServiceClient.getMemo({ - name: memoCommentPayload.memo, - }); - setCommentMemo(comment); - - setSenderName(notification.sender); - setInitialized(true); - } + setSenderName(notification.sender); + setInitialized(true); } catch (error) { handleError(error, () => {}, { - context: "Failed to fetch activity", + context: "Failed to fetch memo comment notification", onError: () => setHasError(true), }); return; } - }, [notification.activityId]); + }, [notification.payload, notification.sender]); const handleNavigateToMemo = async () => { if (!relatedMemo) { diff --git a/web/src/connect.ts b/web/src/connect.ts index 9a6d4cff4..e6b123d4b 100644 --- a/web/src/connect.ts +++ b/web/src/connect.ts @@ -2,7 +2,6 @@ import { timestampDate } from "@bufbuild/protobuf/wkt"; import { Code, ConnectError, createClient, type Interceptor } from "@connectrpc/connect"; import { createConnectTransport } from "@connectrpc/connect-web"; import { getAccessToken, hasStoredToken, isTokenExpired, REQUEST_TOKEN_EXPIRY_BUFFER_MS, setAccessToken } from "./auth-state"; -import { ActivityService } from "./types/proto/api/v1/activity_service_pb"; import { AttachmentService } from "./types/proto/api/v1/attachment_service_pb"; import { AuthService } from "./types/proto/api/v1/auth_service_pb"; import { IdentityProviderService } from "./types/proto/api/v1/idp_service_pb"; @@ -197,7 +196,6 @@ export const userServiceClient = createClient(UserService, transport); export const memoServiceClient = createClient(MemoService, transport); export const attachmentServiceClient = createClient(AttachmentService, transport); export const shortcutServiceClient = createClient(ShortcutService, transport); -export const activityServiceClient = createClient(ActivityService, transport); // Configuration service clients export const identityProviderServiceClient = createClient(IdentityProviderService, transport); diff --git a/web/src/helpers/resource-names.ts b/web/src/helpers/resource-names.ts index 3e9d0bfcc..d561d1b6d 100644 --- a/web/src/helpers/resource-names.ts +++ b/web/src/helpers/resource-names.ts @@ -6,7 +6,6 @@ export const instanceSettingNamePrefix = "instance/settings/"; export const userNamePrefix = "users/"; export const memoNamePrefix = "memos/"; export const identityProviderNamePrefix = "identity-providers/"; -export const activityNamePrefix = "activities/"; export const extractUserIdFromName = (name: string) => { return name.split(userNamePrefix).pop() || ""; diff --git a/web/src/types/proto/api/v1/activity_service_pb.ts b/web/src/types/proto/api/v1/activity_service_pb.ts deleted file mode 100644 index 75e0ea9cb..000000000 --- a/web/src/types/proto/api/v1/activity_service_pb.ts +++ /dev/null @@ -1,304 +0,0 @@ -// @generated by protoc-gen-es v2.11.0 with parameter "target=ts" -// @generated from file api/v1/activity_service.proto (package memos.api.v1, syntax proto3) -/* eslint-disable */ - -import type { GenEnum, GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2"; -import { enumDesc, fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2"; -import { file_google_api_annotations } from "../../google/api/annotations_pb"; -import { file_google_api_client } from "../../google/api/client_pb"; -import { file_google_api_field_behavior } from "../../google/api/field_behavior_pb"; -import { file_google_api_resource } from "../../google/api/resource_pb"; -import type { Timestamp } from "@bufbuild/protobuf/wkt"; -import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt"; -import type { Message } from "@bufbuild/protobuf"; - -/** - * Describes the file api/v1/activity_service.proto. - */ -export const file_api_v1_activity_service: GenFile = /*@__PURE__*/ - fileDesc("Ch1hcGkvdjEvYWN0aXZpdHlfc2VydmljZS5wcm90bxIMbWVtb3MuYXBpLnYxIsEDCghBY3Rpdml0eRIUCgRuYW1lGAEgASgJQgbgQQPgQQgSFAoHY3JlYXRvchgCIAEoCUID4EEDEi4KBHR5cGUYAyABKA4yGy5tZW1vcy5hcGkudjEuQWN0aXZpdHkuVHlwZUID4EEDEjAKBWxldmVsGAQgASgOMhwubWVtb3MuYXBpLnYxLkFjdGl2aXR5LkxldmVsQgPgQQMSNAoLY3JlYXRlX3RpbWUYBSABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wQgPgQQMSMwoHcGF5bG9hZBgGIAEoCzIdLm1lbW9zLmFwaS52MS5BY3Rpdml0eVBheWxvYWRCA+BBAyIuCgRUeXBlEhQKEFRZUEVfVU5TUEVDSUZJRUQQABIQCgxNRU1PX0NPTU1FTlQQASI9CgVMZXZlbBIVChFMRVZFTF9VTlNQRUNJRklFRBAAEggKBElORk8QARIICgRXQVJOEAISCQoFRVJST1IQAzpN6kFKChVtZW1vcy5hcGkudjEvQWN0aXZpdHkSFWFjdGl2aXRpZXMve2FjdGl2aXR5fRoEbmFtZSoKYWN0aXZpdGllczIIYWN0aXZpdHkiXgoPQWN0aXZpdHlQYXlsb2FkEkAKDG1lbW9fY29tbWVudBgBIAEoCzIoLm1lbW9zLmFwaS52MS5BY3Rpdml0eU1lbW9Db21tZW50UGF5bG9hZEgAQgkKB3BheWxvYWQiQAoaQWN0aXZpdHlNZW1vQ29tbWVudFBheWxvYWQSDAoEbWVtbxgBIAEoCRIUCgxyZWxhdGVkX21lbW8YAiABKAkiPgoVTGlzdEFjdGl2aXRpZXNSZXF1ZXN0EhEKCXBhZ2Vfc2l6ZRgBIAEoBRISCgpwYWdlX3Rva2VuGAIgASgJIl0KFkxpc3RBY3Rpdml0aWVzUmVzcG9uc2USKgoKYWN0aXZpdGllcxgBIAMoCzIWLm1lbW9zLmFwaS52MS5BY3Rpdml0eRIXCg9uZXh0X3BhZ2VfdG9rZW4YAiABKAkiQQoSR2V0QWN0aXZpdHlSZXF1ZXN0EisKBG5hbWUYASABKAlCHeBBAvpBFwoVbWVtb3MuYXBpLnYxL0FjdGl2aXR5Mv8BCg9BY3Rpdml0eVNlcnZpY2USdwoOTGlzdEFjdGl2aXRpZXMSIy5tZW1vcy5hcGkudjEuTGlzdEFjdGl2aXRpZXNSZXF1ZXN0GiQubWVtb3MuYXBpLnYxLkxpc3RBY3Rpdml0aWVzUmVzcG9uc2UiGoLT5JMCFBISL2FwaS92MS9hY3Rpdml0aWVzEnMKC0dldEFjdGl2aXR5EiAubWVtb3MuYXBpLnYxLkdldEFjdGl2aXR5UmVxdWVzdBoWLm1lbW9zLmFwaS52MS5BY3Rpdml0eSIq2kEEbmFtZYLT5JMCHRIbL2FwaS92MS97bmFtZT1hY3Rpdml0aWVzLyp9QqwBChBjb20ubWVtb3MuYXBpLnYxQhRBY3Rpdml0eVNlcnZpY2VQcm90b1ABWjBnaXRodWIuY29tL3VzZW1lbW9zL21lbW9zL3Byb3RvL2dlbi9hcGkvdjE7YXBpdjGiAgNNQViqAgxNZW1vcy5BcGkuVjHKAgxNZW1vc1xBcGlcVjHiAhhNZW1vc1xBcGlcVjFcR1BCTWV0YWRhdGHqAg5NZW1vczo6QXBpOjpWMWIGcHJvdG8z", [file_google_api_annotations, file_google_api_client, file_google_api_field_behavior, file_google_api_resource, file_google_protobuf_timestamp]); - -/** - * @generated from message memos.api.v1.Activity - */ -export type Activity = Message<"memos.api.v1.Activity"> & { - /** - * The name of the activity. - * Format: activities/{id} - * - * @generated from field: string name = 1; - */ - name: string; - - /** - * The name of the creator. - * Format: users/{user} - * - * @generated from field: string creator = 2; - */ - creator: string; - - /** - * The type of the activity. - * - * @generated from field: memos.api.v1.Activity.Type type = 3; - */ - type: Activity_Type; - - /** - * The level of the activity. - * - * @generated from field: memos.api.v1.Activity.Level level = 4; - */ - level: Activity_Level; - - /** - * The create time of the activity. - * - * @generated from field: google.protobuf.Timestamp create_time = 5; - */ - createTime?: Timestamp; - - /** - * The payload of the activity. - * - * @generated from field: memos.api.v1.ActivityPayload payload = 6; - */ - payload?: ActivityPayload; -}; - -/** - * Describes the message memos.api.v1.Activity. - * Use `create(ActivitySchema)` to create a new message. - */ -export const ActivitySchema: GenMessage = /*@__PURE__*/ - messageDesc(file_api_v1_activity_service, 0); - -/** - * Activity types. - * - * @generated from enum memos.api.v1.Activity.Type - */ -export enum Activity_Type { - /** - * Unspecified type. - * - * @generated from enum value: TYPE_UNSPECIFIED = 0; - */ - TYPE_UNSPECIFIED = 0, - - /** - * Memo comment activity. - * - * @generated from enum value: MEMO_COMMENT = 1; - */ - MEMO_COMMENT = 1, -} - -/** - * Describes the enum memos.api.v1.Activity.Type. - */ -export const Activity_TypeSchema: GenEnum = /*@__PURE__*/ - enumDesc(file_api_v1_activity_service, 0, 0); - -/** - * Activity levels. - * - * @generated from enum memos.api.v1.Activity.Level - */ -export enum Activity_Level { - /** - * Unspecified level. - * - * @generated from enum value: LEVEL_UNSPECIFIED = 0; - */ - LEVEL_UNSPECIFIED = 0, - - /** - * Info level. - * - * @generated from enum value: INFO = 1; - */ - INFO = 1, - - /** - * Warn level. - * - * @generated from enum value: WARN = 2; - */ - WARN = 2, - - /** - * Error level. - * - * @generated from enum value: ERROR = 3; - */ - ERROR = 3, -} - -/** - * Describes the enum memos.api.v1.Activity.Level. - */ -export const Activity_LevelSchema: GenEnum = /*@__PURE__*/ - enumDesc(file_api_v1_activity_service, 0, 1); - -/** - * @generated from message memos.api.v1.ActivityPayload - */ -export type ActivityPayload = Message<"memos.api.v1.ActivityPayload"> & { - /** - * @generated from oneof memos.api.v1.ActivityPayload.payload - */ - payload: { - /** - * Memo comment activity payload. - * - * @generated from field: memos.api.v1.ActivityMemoCommentPayload memo_comment = 1; - */ - value: ActivityMemoCommentPayload; - case: "memoComment"; - } | { case: undefined; value?: undefined }; -}; - -/** - * Describes the message memos.api.v1.ActivityPayload. - * Use `create(ActivityPayloadSchema)` to create a new message. - */ -export const ActivityPayloadSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_api_v1_activity_service, 1); - -/** - * ActivityMemoCommentPayload represents the payload of a memo comment activity. - * - * @generated from message memos.api.v1.ActivityMemoCommentPayload - */ -export type ActivityMemoCommentPayload = Message<"memos.api.v1.ActivityMemoCommentPayload"> & { - /** - * The memo name of comment. - * Format: memos/{memo} - * - * @generated from field: string memo = 1; - */ - memo: string; - - /** - * The name of related memo. - * Format: memos/{memo} - * - * @generated from field: string related_memo = 2; - */ - relatedMemo: string; -}; - -/** - * Describes the message memos.api.v1.ActivityMemoCommentPayload. - * Use `create(ActivityMemoCommentPayloadSchema)` to create a new message. - */ -export const ActivityMemoCommentPayloadSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_api_v1_activity_service, 2); - -/** - * @generated from message memos.api.v1.ListActivitiesRequest - */ -export type ListActivitiesRequest = Message<"memos.api.v1.ListActivitiesRequest"> & { - /** - * The maximum number of activities to return. - * The service may return fewer than this value. - * If unspecified, at most 100 activities will be returned. - * The maximum value is 1000; values above 1000 will be coerced to 1000. - * - * @generated from field: int32 page_size = 1; - */ - pageSize: number; - - /** - * A page token, received from a previous `ListActivities` call. - * Provide this to retrieve the subsequent page. - * - * @generated from field: string page_token = 2; - */ - pageToken: string; -}; - -/** - * Describes the message memos.api.v1.ListActivitiesRequest. - * Use `create(ListActivitiesRequestSchema)` to create a new message. - */ -export const ListActivitiesRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_api_v1_activity_service, 3); - -/** - * @generated from message memos.api.v1.ListActivitiesResponse - */ -export type ListActivitiesResponse = Message<"memos.api.v1.ListActivitiesResponse"> & { - /** - * The activities. - * - * @generated from field: repeated memos.api.v1.Activity activities = 1; - */ - activities: Activity[]; - - /** - * A token to retrieve the next page of results. - * Pass this value in the page_token field in the subsequent call to `ListActivities` - * method to retrieve the next page of results. - * - * @generated from field: string next_page_token = 2; - */ - nextPageToken: string; -}; - -/** - * Describes the message memos.api.v1.ListActivitiesResponse. - * Use `create(ListActivitiesResponseSchema)` to create a new message. - */ -export const ListActivitiesResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_api_v1_activity_service, 4); - -/** - * @generated from message memos.api.v1.GetActivityRequest - */ -export type GetActivityRequest = Message<"memos.api.v1.GetActivityRequest"> & { - /** - * The name of the activity. - * Format: activities/{id}, id is the system generated auto-incremented id. - * - * @generated from field: string name = 1; - */ - name: string; -}; - -/** - * Describes the message memos.api.v1.GetActivityRequest. - * Use `create(GetActivityRequestSchema)` to create a new message. - */ -export const GetActivityRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_api_v1_activity_service, 5); - -/** - * @generated from service memos.api.v1.ActivityService - */ -export const ActivityService: GenService<{ - /** - * ListActivities returns a list of activities. - * - * @generated from rpc memos.api.v1.ActivityService.ListActivities - */ - listActivities: { - methodKind: "unary"; - input: typeof ListActivitiesRequestSchema; - output: typeof ListActivitiesResponseSchema; - }, - /** - * GetActivity returns the activity with the given id. - * - * @generated from rpc memos.api.v1.ActivityService.GetActivity - */ - getActivity: { - methodKind: "unary"; - input: typeof GetActivityRequestSchema; - output: typeof ActivitySchema; - }, -}> = /*@__PURE__*/ - serviceDesc(file_api_v1_activity_service, 0); - diff --git a/web/src/types/proto/api/v1/user_service_pb.ts b/web/src/types/proto/api/v1/user_service_pb.ts index 46c748123..4a1bc85f6 100644 --- a/web/src/types/proto/api/v1/user_service_pb.ts +++ b/web/src/types/proto/api/v1/user_service_pb.ts @@ -18,7 +18,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file api/v1/user_service.proto. */ export const file_api_v1_user_service: GenFile = /*@__PURE__*/ - fileDesc("ChlhcGkvdjEvdXNlcl9zZXJ2aWNlLnByb3RvEgxtZW1vcy5hcGkudjEi1gMKBFVzZXISEQoEbmFtZRgBIAEoCUID4EEIEioKBHJvbGUYAiABKA4yFy5tZW1vcy5hcGkudjEuVXNlci5Sb2xlQgPgQQISFQoIdXNlcm5hbWUYAyABKAlCA+BBAhISCgVlbWFpbBgEIAEoCUID4EEBEhkKDGRpc3BsYXlfbmFtZRgFIAEoCUID4EEBEhcKCmF2YXRhcl91cmwYBiABKAlCA+BBARIYCgtkZXNjcmlwdGlvbhgHIAEoCUID4EEBEhUKCHBhc3N3b3JkGAggASgJQgPgQQQSJwoFc3RhdGUYCSABKA4yEy5tZW1vcy5hcGkudjEuU3RhdGVCA+BBAhI0CgtjcmVhdGVfdGltZRgKIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBAxI0Cgt1cGRhdGVfdGltZRgLIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBAyIxCgRSb2xlEhQKEFJPTEVfVU5TUEVDSUZJRUQQABIJCgVBRE1JThACEggKBFVTRVIQAzo36kE0ChFtZW1vcy5hcGkudjEvVXNlchIMdXNlcnMve3VzZXJ9GgRuYW1lKgV1c2VyczIEdXNlciJzChBMaXN0VXNlcnNSZXF1ZXN0EhYKCXBhZ2Vfc2l6ZRgBIAEoBUID4EEBEhcKCnBhZ2VfdG9rZW4YAiABKAlCA+BBARITCgZmaWx0ZXIYAyABKAlCA+BBARIZCgxzaG93X2RlbGV0ZWQYBCABKAhCA+BBASJjChFMaXN0VXNlcnNSZXNwb25zZRIhCgV1c2VycxgBIAMoCzISLm1lbW9zLmFwaS52MS5Vc2VyEhcKD25leHRfcGFnZV90b2tlbhgCIAEoCRISCgp0b3RhbF9zaXplGAMgASgFIm0KDkdldFVzZXJSZXF1ZXN0EicKBG5hbWUYASABKAlCGeBBAvpBEwoRbWVtb3MuYXBpLnYxL1VzZXISMgoJcmVhZF9tYXNrGAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLkZpZWxkTWFza0ID4EEBIogBChFDcmVhdGVVc2VyUmVxdWVzdBIoCgR1c2VyGAEgASgLMhIubWVtb3MuYXBpLnYxLlVzZXJCBuBBAuBBBBIUCgd1c2VyX2lkGAIgASgJQgPgQQESGgoNdmFsaWRhdGVfb25seRgDIAEoCEID4EEBEhcKCnJlcXVlc3RfaWQYBCABKAlCA+BBASKMAQoRVXBkYXRlVXNlclJlcXVlc3QSJQoEdXNlchgBIAEoCzISLm1lbW9zLmFwaS52MS5Vc2VyQgPgQQISNAoLdXBkYXRlX21hc2sYAiABKAsyGi5nb29nbGUucHJvdG9idWYuRmllbGRNYXNrQgPgQQISGgoNYWxsb3dfbWlzc2luZxgDIAEoCEID4EEBIlAKEURlbGV0ZVVzZXJSZXF1ZXN0EicKBG5hbWUYASABKAlCGeBBAvpBEwoRbWVtb3MuYXBpLnYxL1VzZXISEgoFZm9yY2UYAiABKAhCA+BBASLYAwoJVXNlclN0YXRzEhEKBG5hbWUYASABKAlCA+BBCBI7ChdtZW1vX2Rpc3BsYXlfdGltZXN0YW1wcxgCIAMoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASPgoPbWVtb190eXBlX3N0YXRzGAMgASgLMiUubWVtb3MuYXBpLnYxLlVzZXJTdGF0cy5NZW1vVHlwZVN0YXRzEjgKCXRhZ19jb3VudBgEIAMoCzIlLm1lbW9zLmFwaS52MS5Vc2VyU3RhdHMuVGFnQ291bnRFbnRyeRIUCgxwaW5uZWRfbWVtb3MYBSADKAkSGAoQdG90YWxfbWVtb19jb3VudBgGIAEoBRovCg1UYWdDb3VudEVudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoBToCOAEaXwoNTWVtb1R5cGVTdGF0cxISCgpsaW5rX2NvdW50GAEgASgFEhIKCmNvZGVfY291bnQYAiABKAUSEgoKdG9kb19jb3VudBgDIAEoBRISCgp1bmRvX2NvdW50GAQgASgFOj/qQTwKFm1lbW9zLmFwaS52MS9Vc2VyU3RhdHMSDHVzZXJzL3t1c2VyfSoJdXNlclN0YXRzMgl1c2VyU3RhdHMiPgoTR2V0VXNlclN0YXRzUmVxdWVzdBInCgRuYW1lGAEgASgJQhngQQL6QRMKEW1lbW9zLmFwaS52MS9Vc2VyIhkKF0xpc3RBbGxVc2VyU3RhdHNSZXF1ZXN0IkIKGExpc3RBbGxVc2VyU3RhdHNSZXNwb25zZRImCgVzdGF0cxgBIAMoCzIXLm1lbW9zLmFwaS52MS5Vc2VyU3RhdHMi4AMKC1VzZXJTZXR0aW5nEhEKBG5hbWUYASABKAlCA+BBCBJDCg9nZW5lcmFsX3NldHRpbmcYAiABKAsyKC5tZW1vcy5hcGkudjEuVXNlclNldHRpbmcuR2VuZXJhbFNldHRpbmdIABJFChB3ZWJob29rc19zZXR0aW5nGAUgASgLMikubWVtb3MuYXBpLnYxLlVzZXJTZXR0aW5nLldlYmhvb2tzU2V0dGluZ0gAGlcKDkdlbmVyYWxTZXR0aW5nEhMKBmxvY2FsZRgBIAEoCUID4EEBEhwKD21lbW9fdmlzaWJpbGl0eRgDIAEoCUID4EEBEhIKBXRoZW1lGAQgASgJQgPgQQEaPgoPV2ViaG9va3NTZXR0aW5nEisKCHdlYmhvb2tzGAEgAygLMhkubWVtb3MuYXBpLnYxLlVzZXJXZWJob29rIjUKA0tleRITCg9LRVlfVU5TUEVDSUZJRUQQABILCgdHRU5FUkFMEAESDAoIV0VCSE9PS1MQBDpZ6kFWChhtZW1vcy5hcGkudjEvVXNlclNldHRpbmcSH3VzZXJzL3t1c2VyfS9zZXR0aW5ncy97c2V0dGluZ30qDHVzZXJTZXR0aW5nczILdXNlclNldHRpbmdCBwoFdmFsdWUiRwoVR2V0VXNlclNldHRpbmdSZXF1ZXN0Ei4KBG5hbWUYASABKAlCIOBBAvpBGgoYbWVtb3MuYXBpLnYxL1VzZXJTZXR0aW5nIoEBChhVcGRhdGVVc2VyU2V0dGluZ1JlcXVlc3QSLwoHc2V0dGluZxgBIAEoCzIZLm1lbW9zLmFwaS52MS5Vc2VyU2V0dGluZ0ID4EECEjQKC3VwZGF0ZV9tYXNrGAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLkZpZWxkTWFza0ID4EECInUKF0xpc3RVc2VyU2V0dGluZ3NSZXF1ZXN0EikKBnBhcmVudBgBIAEoCUIZ4EEC+kETChFtZW1vcy5hcGkudjEvVXNlchIWCglwYWdlX3NpemUYAiABKAVCA+BBARIXCgpwYWdlX3Rva2VuGAMgASgJQgPgQQEidAoYTGlzdFVzZXJTZXR0aW5nc1Jlc3BvbnNlEisKCHNldHRpbmdzGAEgAygLMhkubWVtb3MuYXBpLnYxLlVzZXJTZXR0aW5nEhcKD25leHRfcGFnZV90b2tlbhgCIAEoCRISCgp0b3RhbF9zaXplGAMgASgFIvICChNQZXJzb25hbEFjY2Vzc1Rva2VuEhEKBG5hbWUYASABKAlCA+BBCBIYCgtkZXNjcmlwdGlvbhgCIAEoCUID4EEBEjMKCmNyZWF0ZWRfYXQYAyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wQgPgQQMSMwoKZXhwaXJlc19hdBgEIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBARI1CgxsYXN0X3VzZWRfYXQYBSABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wQgPgQQM6jAHqQYgBCiBtZW1vcy5hcGkudjEvUGVyc29uYWxBY2Nlc3NUb2tlbhI5dXNlcnMve3VzZXJ9L3BlcnNvbmFsQWNjZXNzVG9rZW5zL3twZXJzb25hbF9hY2Nlc3NfdG9rZW59KhRwZXJzb25hbEFjY2Vzc1Rva2VuczITcGVyc29uYWxBY2Nlc3NUb2tlbiJ9Ch9MaXN0UGVyc29uYWxBY2Nlc3NUb2tlbnNSZXF1ZXN0EikKBnBhcmVudBgBIAEoCUIZ4EEC+kETChFtZW1vcy5hcGkudjEvVXNlchIWCglwYWdlX3NpemUYAiABKAVCA+BBARIXCgpwYWdlX3Rva2VuGAMgASgJQgPgQQEikgEKIExpc3RQZXJzb25hbEFjY2Vzc1Rva2Vuc1Jlc3BvbnNlEkEKFnBlcnNvbmFsX2FjY2Vzc190b2tlbnMYASADKAsyIS5tZW1vcy5hcGkudjEuUGVyc29uYWxBY2Nlc3NUb2tlbhIXCg9uZXh0X3BhZ2VfdG9rZW4YAiABKAkSEgoKdG90YWxfc2l6ZRgDIAEoBSKFAQogQ3JlYXRlUGVyc29uYWxBY2Nlc3NUb2tlblJlcXVlc3QSKQoGcGFyZW50GAEgASgJQhngQQL6QRMKEW1lbW9zLmFwaS52MS9Vc2VyEhgKC2Rlc2NyaXB0aW9uGAIgASgJQgPgQQESHAoPZXhwaXJlc19pbl9kYXlzGAMgASgFQgPgQQEidAohQ3JlYXRlUGVyc29uYWxBY2Nlc3NUb2tlblJlc3BvbnNlEkAKFXBlcnNvbmFsX2FjY2Vzc190b2tlbhgBIAEoCzIhLm1lbW9zLmFwaS52MS5QZXJzb25hbEFjY2Vzc1Rva2VuEg0KBXRva2VuGAIgASgJIloKIERlbGV0ZVBlcnNvbmFsQWNjZXNzVG9rZW5SZXF1ZXN0EjYKBG5hbWUYASABKAlCKOBBAvpBIgogbWVtb3MuYXBpLnYxL1BlcnNvbmFsQWNjZXNzVG9rZW4iqgEKC1VzZXJXZWJob29rEgwKBG5hbWUYASABKAkSCwoDdXJsGAIgASgJEhQKDGRpc3BsYXlfbmFtZRgDIAEoCRI0CgtjcmVhdGVfdGltZRgEIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBAxI0Cgt1cGRhdGVfdGltZRgFIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBAyIuChdMaXN0VXNlcldlYmhvb2tzUmVxdWVzdBITCgZwYXJlbnQYASABKAlCA+BBAiJHChhMaXN0VXNlcldlYmhvb2tzUmVzcG9uc2USKwoId2ViaG9va3MYASADKAsyGS5tZW1vcy5hcGkudjEuVXNlcldlYmhvb2siYAoYQ3JlYXRlVXNlcldlYmhvb2tSZXF1ZXN0EhMKBnBhcmVudBgBIAEoCUID4EECEi8KB3dlYmhvb2sYAiABKAsyGS5tZW1vcy5hcGkudjEuVXNlcldlYmhvb2tCA+BBAiJ8ChhVcGRhdGVVc2VyV2ViaG9va1JlcXVlc3QSLwoHd2ViaG9vaxgBIAEoCzIZLm1lbW9zLmFwaS52MS5Vc2VyV2ViaG9va0ID4EECEi8KC3VwZGF0ZV9tYXNrGAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLkZpZWxkTWFzayItChhEZWxldGVVc2VyV2ViaG9va1JlcXVlc3QSEQoEbmFtZRgBIAEoCUID4EECIooEChBVc2VyTm90aWZpY2F0aW9uEhQKBG5hbWUYASABKAlCBuBBA+BBCBIpCgZzZW5kZXIYAiABKAlCGeBBA/pBEwoRbWVtb3MuYXBpLnYxL1VzZXISOgoGc3RhdHVzGAMgASgOMiUubWVtb3MuYXBpLnYxLlVzZXJOb3RpZmljYXRpb24uU3RhdHVzQgPgQQESNAoLY3JlYXRlX3RpbWUYBCABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wQgPgQQMSNgoEdHlwZRgFIAEoDjIjLm1lbW9zLmFwaS52MS5Vc2VyTm90aWZpY2F0aW9uLlR5cGVCA+BBAxIdCgthY3Rpdml0eV9pZBgGIAEoBUID4EEBSACIAQEiOgoGU3RhdHVzEhYKElNUQVRVU19VTlNQRUNJRklFRBAAEgoKBlVOUkVBRBABEgwKCEFSQ0hJVkVEEAIiLgoEVHlwZRIUChBUWVBFX1VOU1BFQ0lGSUVEEAASEAoMTUVNT19DT01NRU5UEAE6cOpBbQodbWVtb3MuYXBpLnYxL1VzZXJOb3RpZmljYXRpb24SKXVzZXJzL3t1c2VyfS9ub3RpZmljYXRpb25zL3tub3RpZmljYXRpb259GgRuYW1lKg1ub3RpZmljYXRpb25zMgxub3RpZmljYXRpb25CDgoMX2FjdGl2aXR5X2lkIo8BChxMaXN0VXNlck5vdGlmaWNhdGlvbnNSZXF1ZXN0EikKBnBhcmVudBgBIAEoCUIZ4EEC+kETChFtZW1vcy5hcGkudjEvVXNlchIWCglwYWdlX3NpemUYAiABKAVCA+BBARIXCgpwYWdlX3Rva2VuGAMgASgJQgPgQQESEwoGZmlsdGVyGAQgASgJQgPgQQEibwodTGlzdFVzZXJOb3RpZmljYXRpb25zUmVzcG9uc2USNQoNbm90aWZpY2F0aW9ucxgBIAMoCzIeLm1lbW9zLmFwaS52MS5Vc2VyTm90aWZpY2F0aW9uEhcKD25leHRfcGFnZV90b2tlbhgCIAEoCSKQAQodVXBkYXRlVXNlck5vdGlmaWNhdGlvblJlcXVlc3QSOQoMbm90aWZpY2F0aW9uGAEgASgLMh4ubWVtb3MuYXBpLnYxLlVzZXJOb3RpZmljYXRpb25CA+BBAhI0Cgt1cGRhdGVfbWFzaxgCIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE1hc2tCA+BBAiJUCh1EZWxldGVVc2VyTm90aWZpY2F0aW9uUmVxdWVzdBIzCgRuYW1lGAEgASgJQiXgQQL6QR8KHW1lbW9zLmFwaS52MS9Vc2VyTm90aWZpY2F0aW9uMoMXCgtVc2VyU2VydmljZRJjCglMaXN0VXNlcnMSHi5tZW1vcy5hcGkudjEuTGlzdFVzZXJzUmVxdWVzdBofLm1lbW9zLmFwaS52MS5MaXN0VXNlcnNSZXNwb25zZSIVgtPkkwIPEg0vYXBpL3YxL3VzZXJzEmIKB0dldFVzZXISHC5tZW1vcy5hcGkudjEuR2V0VXNlclJlcXVlc3QaEi5tZW1vcy5hcGkudjEuVXNlciIl2kEEbmFtZYLT5JMCGBIWL2FwaS92MS97bmFtZT11c2Vycy8qfRJlCgpDcmVhdGVVc2VyEh8ubWVtb3MuYXBpLnYxLkNyZWF0ZVVzZXJSZXF1ZXN0GhIubWVtb3MuYXBpLnYxLlVzZXIiItpBBHVzZXKC0+STAhU6BHVzZXIiDS9hcGkvdjEvdXNlcnMSfwoKVXBkYXRlVXNlchIfLm1lbW9zLmFwaS52MS5VcGRhdGVVc2VyUmVxdWVzdBoSLm1lbW9zLmFwaS52MS5Vc2VyIjzaQRB1c2VyLHVwZGF0ZV9tYXNrgtPkkwIjOgR1c2VyMhsvYXBpL3YxL3t1c2VyLm5hbWU9dXNlcnMvKn0SbAoKRGVsZXRlVXNlchIfLm1lbW9zLmFwaS52MS5EZWxldGVVc2VyUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSIl2kEEbmFtZYLT5JMCGCoWL2FwaS92MS97bmFtZT11c2Vycy8qfRJ+ChBMaXN0QWxsVXNlclN0YXRzEiUubWVtb3MuYXBpLnYxLkxpc3RBbGxVc2VyU3RhdHNSZXF1ZXN0GiYubWVtb3MuYXBpLnYxLkxpc3RBbGxVc2VyU3RhdHNSZXNwb25zZSIbgtPkkwIVEhMvYXBpL3YxL3VzZXJzOnN0YXRzEnoKDEdldFVzZXJTdGF0cxIhLm1lbW9zLmFwaS52MS5HZXRVc2VyU3RhdHNSZXF1ZXN0GhcubWVtb3MuYXBpLnYxLlVzZXJTdGF0cyIu2kEEbmFtZYLT5JMCIRIfL2FwaS92MS97bmFtZT11c2Vycy8qfTpnZXRTdGF0cxKCAQoOR2V0VXNlclNldHRpbmcSIy5tZW1vcy5hcGkudjEuR2V0VXNlclNldHRpbmdSZXF1ZXN0GhkubWVtb3MuYXBpLnYxLlVzZXJTZXR0aW5nIjDaQQRuYW1lgtPkkwIjEiEvYXBpL3YxL3tuYW1lPXVzZXJzLyovc2V0dGluZ3MvKn0SqAEKEVVwZGF0ZVVzZXJTZXR0aW5nEiYubWVtb3MuYXBpLnYxLlVwZGF0ZVVzZXJTZXR0aW5nUmVxdWVzdBoZLm1lbW9zLmFwaS52MS5Vc2VyU2V0dGluZyJQ2kETc2V0dGluZyx1cGRhdGVfbWFza4LT5JMCNDoHc2V0dGluZzIpL2FwaS92MS97c2V0dGluZy5uYW1lPXVzZXJzLyovc2V0dGluZ3MvKn0SlQEKEExpc3RVc2VyU2V0dGluZ3MSJS5tZW1vcy5hcGkudjEuTGlzdFVzZXJTZXR0aW5nc1JlcXVlc3QaJi5tZW1vcy5hcGkudjEuTGlzdFVzZXJTZXR0aW5nc1Jlc3BvbnNlIjLaQQZwYXJlbnSC0+STAiMSIS9hcGkvdjEve3BhcmVudD11c2Vycy8qfS9zZXR0aW5ncxK5AQoYTGlzdFBlcnNvbmFsQWNjZXNzVG9rZW5zEi0ubWVtb3MuYXBpLnYxLkxpc3RQZXJzb25hbEFjY2Vzc1Rva2Vuc1JlcXVlc3QaLi5tZW1vcy5hcGkudjEuTGlzdFBlcnNvbmFsQWNjZXNzVG9rZW5zUmVzcG9uc2UiPtpBBnBhcmVudILT5JMCLxItL2FwaS92MS97cGFyZW50PXVzZXJzLyp9L3BlcnNvbmFsQWNjZXNzVG9rZW5zErYBChlDcmVhdGVQZXJzb25hbEFjY2Vzc1Rva2VuEi4ubWVtb3MuYXBpLnYxLkNyZWF0ZVBlcnNvbmFsQWNjZXNzVG9rZW5SZXF1ZXN0Gi8ubWVtb3MuYXBpLnYxLkNyZWF0ZVBlcnNvbmFsQWNjZXNzVG9rZW5SZXNwb25zZSI4gtPkkwIyOgEqIi0vYXBpL3YxL3twYXJlbnQ9dXNlcnMvKn0vcGVyc29uYWxBY2Nlc3NUb2tlbnMSoQEKGURlbGV0ZVBlcnNvbmFsQWNjZXNzVG9rZW4SLi5tZW1vcy5hcGkudjEuRGVsZXRlUGVyc29uYWxBY2Nlc3NUb2tlblJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiPNpBBG5hbWWC0+STAi8qLS9hcGkvdjEve25hbWU9dXNlcnMvKi9wZXJzb25hbEFjY2Vzc1Rva2Vucy8qfRKVAQoQTGlzdFVzZXJXZWJob29rcxIlLm1lbW9zLmFwaS52MS5MaXN0VXNlcldlYmhvb2tzUmVxdWVzdBomLm1lbW9zLmFwaS52MS5MaXN0VXNlcldlYmhvb2tzUmVzcG9uc2UiMtpBBnBhcmVudILT5JMCIxIhL2FwaS92MS97cGFyZW50PXVzZXJzLyp9L3dlYmhvb2tzEpsBChFDcmVhdGVVc2VyV2ViaG9vaxImLm1lbW9zLmFwaS52MS5DcmVhdGVVc2VyV2ViaG9va1JlcXVlc3QaGS5tZW1vcy5hcGkudjEuVXNlcldlYmhvb2siQ9pBDnBhcmVudCx3ZWJob29rgtPkkwIsOgd3ZWJob29rIiEvYXBpL3YxL3twYXJlbnQ9dXNlcnMvKn0vd2ViaG9va3MSqAEKEVVwZGF0ZVVzZXJXZWJob29rEiYubWVtb3MuYXBpLnYxLlVwZGF0ZVVzZXJXZWJob29rUmVxdWVzdBoZLm1lbW9zLmFwaS52MS5Vc2VyV2ViaG9vayJQ2kETd2ViaG9vayx1cGRhdGVfbWFza4LT5JMCNDoHd2ViaG9vazIpL2FwaS92MS97d2ViaG9vay5uYW1lPXVzZXJzLyovd2ViaG9va3MvKn0ShQEKEURlbGV0ZVVzZXJXZWJob29rEiYubWVtb3MuYXBpLnYxLkRlbGV0ZVVzZXJXZWJob29rUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSIw2kEEbmFtZYLT5JMCIyohL2FwaS92MS97bmFtZT11c2Vycy8qL3dlYmhvb2tzLyp9EqkBChVMaXN0VXNlck5vdGlmaWNhdGlvbnMSKi5tZW1vcy5hcGkudjEuTGlzdFVzZXJOb3RpZmljYXRpb25zUmVxdWVzdBorLm1lbW9zLmFwaS52MS5MaXN0VXNlck5vdGlmaWNhdGlvbnNSZXNwb25zZSI32kEGcGFyZW50gtPkkwIoEiYvYXBpL3YxL3twYXJlbnQ9dXNlcnMvKn0vbm90aWZpY2F0aW9ucxLLAQoWVXBkYXRlVXNlck5vdGlmaWNhdGlvbhIrLm1lbW9zLmFwaS52MS5VcGRhdGVVc2VyTm90aWZpY2F0aW9uUmVxdWVzdBoeLm1lbW9zLmFwaS52MS5Vc2VyTm90aWZpY2F0aW9uImTaQRhub3RpZmljYXRpb24sdXBkYXRlX21hc2uC0+STAkM6DG5vdGlmaWNhdGlvbjIzL2FwaS92MS97bm90aWZpY2F0aW9uLm5hbWU9dXNlcnMvKi9ub3RpZmljYXRpb25zLyp9EpQBChZEZWxldGVVc2VyTm90aWZpY2F0aW9uEisubWVtb3MuYXBpLnYxLkRlbGV0ZVVzZXJOb3RpZmljYXRpb25SZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IjXaQQRuYW1lgtPkkwIoKiYvYXBpL3YxL3tuYW1lPXVzZXJzLyovbm90aWZpY2F0aW9ucy8qfUKoAQoQY29tLm1lbW9zLmFwaS52MUIQVXNlclNlcnZpY2VQcm90b1ABWjBnaXRodWIuY29tL3VzZW1lbW9zL21lbW9zL3Byb3RvL2dlbi9hcGkvdjE7YXBpdjGiAgNNQViqAgxNZW1vcy5BcGkuVjHKAgxNZW1vc1xBcGlcVjHiAhhNZW1vc1xBcGlcVjFcR1BCTWV0YWRhdGHqAg5NZW1vczo6QXBpOjpWMWIGcHJvdG8z", [file_api_v1_common, file_google_api_annotations, file_google_api_client, file_google_api_field_behavior, file_google_api_resource, file_google_protobuf_empty, file_google_protobuf_field_mask, file_google_protobuf_timestamp]); + fileDesc("ChlhcGkvdjEvdXNlcl9zZXJ2aWNlLnByb3RvEgxtZW1vcy5hcGkudjEi1gMKBFVzZXISEQoEbmFtZRgBIAEoCUID4EEIEioKBHJvbGUYAiABKA4yFy5tZW1vcy5hcGkudjEuVXNlci5Sb2xlQgPgQQISFQoIdXNlcm5hbWUYAyABKAlCA+BBAhISCgVlbWFpbBgEIAEoCUID4EEBEhkKDGRpc3BsYXlfbmFtZRgFIAEoCUID4EEBEhcKCmF2YXRhcl91cmwYBiABKAlCA+BBARIYCgtkZXNjcmlwdGlvbhgHIAEoCUID4EEBEhUKCHBhc3N3b3JkGAggASgJQgPgQQQSJwoFc3RhdGUYCSABKA4yEy5tZW1vcy5hcGkudjEuU3RhdGVCA+BBAhI0CgtjcmVhdGVfdGltZRgKIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBAxI0Cgt1cGRhdGVfdGltZRgLIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBAyIxCgRSb2xlEhQKEFJPTEVfVU5TUEVDSUZJRUQQABIJCgVBRE1JThACEggKBFVTRVIQAzo36kE0ChFtZW1vcy5hcGkudjEvVXNlchIMdXNlcnMve3VzZXJ9GgRuYW1lKgV1c2VyczIEdXNlciJzChBMaXN0VXNlcnNSZXF1ZXN0EhYKCXBhZ2Vfc2l6ZRgBIAEoBUID4EEBEhcKCnBhZ2VfdG9rZW4YAiABKAlCA+BBARITCgZmaWx0ZXIYAyABKAlCA+BBARIZCgxzaG93X2RlbGV0ZWQYBCABKAhCA+BBASJjChFMaXN0VXNlcnNSZXNwb25zZRIhCgV1c2VycxgBIAMoCzISLm1lbW9zLmFwaS52MS5Vc2VyEhcKD25leHRfcGFnZV90b2tlbhgCIAEoCRISCgp0b3RhbF9zaXplGAMgASgFIm0KDkdldFVzZXJSZXF1ZXN0EicKBG5hbWUYASABKAlCGeBBAvpBEwoRbWVtb3MuYXBpLnYxL1VzZXISMgoJcmVhZF9tYXNrGAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLkZpZWxkTWFza0ID4EEBIogBChFDcmVhdGVVc2VyUmVxdWVzdBIoCgR1c2VyGAEgASgLMhIubWVtb3MuYXBpLnYxLlVzZXJCBuBBAuBBBBIUCgd1c2VyX2lkGAIgASgJQgPgQQESGgoNdmFsaWRhdGVfb25seRgDIAEoCEID4EEBEhcKCnJlcXVlc3RfaWQYBCABKAlCA+BBASKMAQoRVXBkYXRlVXNlclJlcXVlc3QSJQoEdXNlchgBIAEoCzISLm1lbW9zLmFwaS52MS5Vc2VyQgPgQQISNAoLdXBkYXRlX21hc2sYAiABKAsyGi5nb29nbGUucHJvdG9idWYuRmllbGRNYXNrQgPgQQISGgoNYWxsb3dfbWlzc2luZxgDIAEoCEID4EEBIlAKEURlbGV0ZVVzZXJSZXF1ZXN0EicKBG5hbWUYASABKAlCGeBBAvpBEwoRbWVtb3MuYXBpLnYxL1VzZXISEgoFZm9yY2UYAiABKAhCA+BBASLYAwoJVXNlclN0YXRzEhEKBG5hbWUYASABKAlCA+BBCBI7ChdtZW1vX2Rpc3BsYXlfdGltZXN0YW1wcxgCIAMoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASPgoPbWVtb190eXBlX3N0YXRzGAMgASgLMiUubWVtb3MuYXBpLnYxLlVzZXJTdGF0cy5NZW1vVHlwZVN0YXRzEjgKCXRhZ19jb3VudBgEIAMoCzIlLm1lbW9zLmFwaS52MS5Vc2VyU3RhdHMuVGFnQ291bnRFbnRyeRIUCgxwaW5uZWRfbWVtb3MYBSADKAkSGAoQdG90YWxfbWVtb19jb3VudBgGIAEoBRovCg1UYWdDb3VudEVudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoBToCOAEaXwoNTWVtb1R5cGVTdGF0cxISCgpsaW5rX2NvdW50GAEgASgFEhIKCmNvZGVfY291bnQYAiABKAUSEgoKdG9kb19jb3VudBgDIAEoBRISCgp1bmRvX2NvdW50GAQgASgFOj/qQTwKFm1lbW9zLmFwaS52MS9Vc2VyU3RhdHMSDHVzZXJzL3t1c2VyfSoJdXNlclN0YXRzMgl1c2VyU3RhdHMiPgoTR2V0VXNlclN0YXRzUmVxdWVzdBInCgRuYW1lGAEgASgJQhngQQL6QRMKEW1lbW9zLmFwaS52MS9Vc2VyIhkKF0xpc3RBbGxVc2VyU3RhdHNSZXF1ZXN0IkIKGExpc3RBbGxVc2VyU3RhdHNSZXNwb25zZRImCgVzdGF0cxgBIAMoCzIXLm1lbW9zLmFwaS52MS5Vc2VyU3RhdHMi4AMKC1VzZXJTZXR0aW5nEhEKBG5hbWUYASABKAlCA+BBCBJDCg9nZW5lcmFsX3NldHRpbmcYAiABKAsyKC5tZW1vcy5hcGkudjEuVXNlclNldHRpbmcuR2VuZXJhbFNldHRpbmdIABJFChB3ZWJob29rc19zZXR0aW5nGAUgASgLMikubWVtb3MuYXBpLnYxLlVzZXJTZXR0aW5nLldlYmhvb2tzU2V0dGluZ0gAGlcKDkdlbmVyYWxTZXR0aW5nEhMKBmxvY2FsZRgBIAEoCUID4EEBEhwKD21lbW9fdmlzaWJpbGl0eRgDIAEoCUID4EEBEhIKBXRoZW1lGAQgASgJQgPgQQEaPgoPV2ViaG9va3NTZXR0aW5nEisKCHdlYmhvb2tzGAEgAygLMhkubWVtb3MuYXBpLnYxLlVzZXJXZWJob29rIjUKA0tleRITCg9LRVlfVU5TUEVDSUZJRUQQABILCgdHRU5FUkFMEAESDAoIV0VCSE9PS1MQBDpZ6kFWChhtZW1vcy5hcGkudjEvVXNlclNldHRpbmcSH3VzZXJzL3t1c2VyfS9zZXR0aW5ncy97c2V0dGluZ30qDHVzZXJTZXR0aW5nczILdXNlclNldHRpbmdCBwoFdmFsdWUiRwoVR2V0VXNlclNldHRpbmdSZXF1ZXN0Ei4KBG5hbWUYASABKAlCIOBBAvpBGgoYbWVtb3MuYXBpLnYxL1VzZXJTZXR0aW5nIoEBChhVcGRhdGVVc2VyU2V0dGluZ1JlcXVlc3QSLwoHc2V0dGluZxgBIAEoCzIZLm1lbW9zLmFwaS52MS5Vc2VyU2V0dGluZ0ID4EECEjQKC3VwZGF0ZV9tYXNrGAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLkZpZWxkTWFza0ID4EECInUKF0xpc3RVc2VyU2V0dGluZ3NSZXF1ZXN0EikKBnBhcmVudBgBIAEoCUIZ4EEC+kETChFtZW1vcy5hcGkudjEvVXNlchIWCglwYWdlX3NpemUYAiABKAVCA+BBARIXCgpwYWdlX3Rva2VuGAMgASgJQgPgQQEidAoYTGlzdFVzZXJTZXR0aW5nc1Jlc3BvbnNlEisKCHNldHRpbmdzGAEgAygLMhkubWVtb3MuYXBpLnYxLlVzZXJTZXR0aW5nEhcKD25leHRfcGFnZV90b2tlbhgCIAEoCRISCgp0b3RhbF9zaXplGAMgASgFIvICChNQZXJzb25hbEFjY2Vzc1Rva2VuEhEKBG5hbWUYASABKAlCA+BBCBIYCgtkZXNjcmlwdGlvbhgCIAEoCUID4EEBEjMKCmNyZWF0ZWRfYXQYAyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wQgPgQQMSMwoKZXhwaXJlc19hdBgEIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBARI1CgxsYXN0X3VzZWRfYXQYBSABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wQgPgQQM6jAHqQYgBCiBtZW1vcy5hcGkudjEvUGVyc29uYWxBY2Nlc3NUb2tlbhI5dXNlcnMve3VzZXJ9L3BlcnNvbmFsQWNjZXNzVG9rZW5zL3twZXJzb25hbF9hY2Nlc3NfdG9rZW59KhRwZXJzb25hbEFjY2Vzc1Rva2VuczITcGVyc29uYWxBY2Nlc3NUb2tlbiJ9Ch9MaXN0UGVyc29uYWxBY2Nlc3NUb2tlbnNSZXF1ZXN0EikKBnBhcmVudBgBIAEoCUIZ4EEC+kETChFtZW1vcy5hcGkudjEvVXNlchIWCglwYWdlX3NpemUYAiABKAVCA+BBARIXCgpwYWdlX3Rva2VuGAMgASgJQgPgQQEikgEKIExpc3RQZXJzb25hbEFjY2Vzc1Rva2Vuc1Jlc3BvbnNlEkEKFnBlcnNvbmFsX2FjY2Vzc190b2tlbnMYASADKAsyIS5tZW1vcy5hcGkudjEuUGVyc29uYWxBY2Nlc3NUb2tlbhIXCg9uZXh0X3BhZ2VfdG9rZW4YAiABKAkSEgoKdG90YWxfc2l6ZRgDIAEoBSKFAQogQ3JlYXRlUGVyc29uYWxBY2Nlc3NUb2tlblJlcXVlc3QSKQoGcGFyZW50GAEgASgJQhngQQL6QRMKEW1lbW9zLmFwaS52MS9Vc2VyEhgKC2Rlc2NyaXB0aW9uGAIgASgJQgPgQQESHAoPZXhwaXJlc19pbl9kYXlzGAMgASgFQgPgQQEidAohQ3JlYXRlUGVyc29uYWxBY2Nlc3NUb2tlblJlc3BvbnNlEkAKFXBlcnNvbmFsX2FjY2Vzc190b2tlbhgBIAEoCzIhLm1lbW9zLmFwaS52MS5QZXJzb25hbEFjY2Vzc1Rva2VuEg0KBXRva2VuGAIgASgJIloKIERlbGV0ZVBlcnNvbmFsQWNjZXNzVG9rZW5SZXF1ZXN0EjYKBG5hbWUYASABKAlCKOBBAvpBIgogbWVtb3MuYXBpLnYxL1BlcnNvbmFsQWNjZXNzVG9rZW4iqgEKC1VzZXJXZWJob29rEgwKBG5hbWUYASABKAkSCwoDdXJsGAIgASgJEhQKDGRpc3BsYXlfbmFtZRgDIAEoCRI0CgtjcmVhdGVfdGltZRgEIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBAxI0Cgt1cGRhdGVfdGltZRgFIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBAyIuChdMaXN0VXNlcldlYmhvb2tzUmVxdWVzdBITCgZwYXJlbnQYASABKAlCA+BBAiJHChhMaXN0VXNlcldlYmhvb2tzUmVzcG9uc2USKwoId2ViaG9va3MYASADKAsyGS5tZW1vcy5hcGkudjEuVXNlcldlYmhvb2siYAoYQ3JlYXRlVXNlcldlYmhvb2tSZXF1ZXN0EhMKBnBhcmVudBgBIAEoCUID4EECEi8KB3dlYmhvb2sYAiABKAsyGS5tZW1vcy5hcGkudjEuVXNlcldlYmhvb2tCA+BBAiJ8ChhVcGRhdGVVc2VyV2ViaG9va1JlcXVlc3QSLwoHd2ViaG9vaxgBIAEoCzIZLm1lbW9zLmFwaS52MS5Vc2VyV2ViaG9va0ID4EECEi8KC3VwZGF0ZV9tYXNrGAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLkZpZWxkTWFzayItChhEZWxldGVVc2VyV2ViaG9va1JlcXVlc3QSEQoEbmFtZRgBIAEoCUID4EECIvAEChBVc2VyTm90aWZpY2F0aW9uEhQKBG5hbWUYASABKAlCBuBBA+BBCBIpCgZzZW5kZXIYAiABKAlCGeBBA/pBEwoRbWVtb3MuYXBpLnYxL1VzZXISOgoGc3RhdHVzGAMgASgOMiUubWVtb3MuYXBpLnYxLlVzZXJOb3RpZmljYXRpb24uU3RhdHVzQgPgQQESNAoLY3JlYXRlX3RpbWUYBCABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wQgPgQQMSNgoEdHlwZRgFIAEoDjIjLm1lbW9zLmFwaS52MS5Vc2VyTm90aWZpY2F0aW9uLlR5cGVCA+BBAxJOCgxtZW1vX2NvbW1lbnQYBiABKAsyMS5tZW1vcy5hcGkudjEuVXNlck5vdGlmaWNhdGlvbi5NZW1vQ29tbWVudFBheWxvYWRCA+BBA0gAGjgKEk1lbW9Db21tZW50UGF5bG9hZBIMCgRtZW1vGAEgASgJEhQKDHJlbGF0ZWRfbWVtbxgCIAEoCSI6CgZTdGF0dXMSFgoSU1RBVFVTX1VOU1BFQ0lGSUVEEAASCgoGVU5SRUFEEAESDAoIQVJDSElWRUQQAiIuCgRUeXBlEhQKEFRZUEVfVU5TUEVDSUZJRUQQABIQCgxNRU1PX0NPTU1FTlQQATpw6kFtCh1tZW1vcy5hcGkudjEvVXNlck5vdGlmaWNhdGlvbhIpdXNlcnMve3VzZXJ9L25vdGlmaWNhdGlvbnMve25vdGlmaWNhdGlvbn0aBG5hbWUqDW5vdGlmaWNhdGlvbnMyDG5vdGlmaWNhdGlvbkIJCgdwYXlsb2FkIo8BChxMaXN0VXNlck5vdGlmaWNhdGlvbnNSZXF1ZXN0EikKBnBhcmVudBgBIAEoCUIZ4EEC+kETChFtZW1vcy5hcGkudjEvVXNlchIWCglwYWdlX3NpemUYAiABKAVCA+BBARIXCgpwYWdlX3Rva2VuGAMgASgJQgPgQQESEwoGZmlsdGVyGAQgASgJQgPgQQEibwodTGlzdFVzZXJOb3RpZmljYXRpb25zUmVzcG9uc2USNQoNbm90aWZpY2F0aW9ucxgBIAMoCzIeLm1lbW9zLmFwaS52MS5Vc2VyTm90aWZpY2F0aW9uEhcKD25leHRfcGFnZV90b2tlbhgCIAEoCSKQAQodVXBkYXRlVXNlck5vdGlmaWNhdGlvblJlcXVlc3QSOQoMbm90aWZpY2F0aW9uGAEgASgLMh4ubWVtb3MuYXBpLnYxLlVzZXJOb3RpZmljYXRpb25CA+BBAhI0Cgt1cGRhdGVfbWFzaxgCIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE1hc2tCA+BBAiJUCh1EZWxldGVVc2VyTm90aWZpY2F0aW9uUmVxdWVzdBIzCgRuYW1lGAEgASgJQiXgQQL6QR8KHW1lbW9zLmFwaS52MS9Vc2VyTm90aWZpY2F0aW9uMoMXCgtVc2VyU2VydmljZRJjCglMaXN0VXNlcnMSHi5tZW1vcy5hcGkudjEuTGlzdFVzZXJzUmVxdWVzdBofLm1lbW9zLmFwaS52MS5MaXN0VXNlcnNSZXNwb25zZSIVgtPkkwIPEg0vYXBpL3YxL3VzZXJzEmIKB0dldFVzZXISHC5tZW1vcy5hcGkudjEuR2V0VXNlclJlcXVlc3QaEi5tZW1vcy5hcGkudjEuVXNlciIl2kEEbmFtZYLT5JMCGBIWL2FwaS92MS97bmFtZT11c2Vycy8qfRJlCgpDcmVhdGVVc2VyEh8ubWVtb3MuYXBpLnYxLkNyZWF0ZVVzZXJSZXF1ZXN0GhIubWVtb3MuYXBpLnYxLlVzZXIiItpBBHVzZXKC0+STAhU6BHVzZXIiDS9hcGkvdjEvdXNlcnMSfwoKVXBkYXRlVXNlchIfLm1lbW9zLmFwaS52MS5VcGRhdGVVc2VyUmVxdWVzdBoSLm1lbW9zLmFwaS52MS5Vc2VyIjzaQRB1c2VyLHVwZGF0ZV9tYXNrgtPkkwIjOgR1c2VyMhsvYXBpL3YxL3t1c2VyLm5hbWU9dXNlcnMvKn0SbAoKRGVsZXRlVXNlchIfLm1lbW9zLmFwaS52MS5EZWxldGVVc2VyUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSIl2kEEbmFtZYLT5JMCGCoWL2FwaS92MS97bmFtZT11c2Vycy8qfRJ+ChBMaXN0QWxsVXNlclN0YXRzEiUubWVtb3MuYXBpLnYxLkxpc3RBbGxVc2VyU3RhdHNSZXF1ZXN0GiYubWVtb3MuYXBpLnYxLkxpc3RBbGxVc2VyU3RhdHNSZXNwb25zZSIbgtPkkwIVEhMvYXBpL3YxL3VzZXJzOnN0YXRzEnoKDEdldFVzZXJTdGF0cxIhLm1lbW9zLmFwaS52MS5HZXRVc2VyU3RhdHNSZXF1ZXN0GhcubWVtb3MuYXBpLnYxLlVzZXJTdGF0cyIu2kEEbmFtZYLT5JMCIRIfL2FwaS92MS97bmFtZT11c2Vycy8qfTpnZXRTdGF0cxKCAQoOR2V0VXNlclNldHRpbmcSIy5tZW1vcy5hcGkudjEuR2V0VXNlclNldHRpbmdSZXF1ZXN0GhkubWVtb3MuYXBpLnYxLlVzZXJTZXR0aW5nIjDaQQRuYW1lgtPkkwIjEiEvYXBpL3YxL3tuYW1lPXVzZXJzLyovc2V0dGluZ3MvKn0SqAEKEVVwZGF0ZVVzZXJTZXR0aW5nEiYubWVtb3MuYXBpLnYxLlVwZGF0ZVVzZXJTZXR0aW5nUmVxdWVzdBoZLm1lbW9zLmFwaS52MS5Vc2VyU2V0dGluZyJQ2kETc2V0dGluZyx1cGRhdGVfbWFza4LT5JMCNDoHc2V0dGluZzIpL2FwaS92MS97c2V0dGluZy5uYW1lPXVzZXJzLyovc2V0dGluZ3MvKn0SlQEKEExpc3RVc2VyU2V0dGluZ3MSJS5tZW1vcy5hcGkudjEuTGlzdFVzZXJTZXR0aW5nc1JlcXVlc3QaJi5tZW1vcy5hcGkudjEuTGlzdFVzZXJTZXR0aW5nc1Jlc3BvbnNlIjLaQQZwYXJlbnSC0+STAiMSIS9hcGkvdjEve3BhcmVudD11c2Vycy8qfS9zZXR0aW5ncxK5AQoYTGlzdFBlcnNvbmFsQWNjZXNzVG9rZW5zEi0ubWVtb3MuYXBpLnYxLkxpc3RQZXJzb25hbEFjY2Vzc1Rva2Vuc1JlcXVlc3QaLi5tZW1vcy5hcGkudjEuTGlzdFBlcnNvbmFsQWNjZXNzVG9rZW5zUmVzcG9uc2UiPtpBBnBhcmVudILT5JMCLxItL2FwaS92MS97cGFyZW50PXVzZXJzLyp9L3BlcnNvbmFsQWNjZXNzVG9rZW5zErYBChlDcmVhdGVQZXJzb25hbEFjY2Vzc1Rva2VuEi4ubWVtb3MuYXBpLnYxLkNyZWF0ZVBlcnNvbmFsQWNjZXNzVG9rZW5SZXF1ZXN0Gi8ubWVtb3MuYXBpLnYxLkNyZWF0ZVBlcnNvbmFsQWNjZXNzVG9rZW5SZXNwb25zZSI4gtPkkwIyOgEqIi0vYXBpL3YxL3twYXJlbnQ9dXNlcnMvKn0vcGVyc29uYWxBY2Nlc3NUb2tlbnMSoQEKGURlbGV0ZVBlcnNvbmFsQWNjZXNzVG9rZW4SLi5tZW1vcy5hcGkudjEuRGVsZXRlUGVyc29uYWxBY2Nlc3NUb2tlblJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiPNpBBG5hbWWC0+STAi8qLS9hcGkvdjEve25hbWU9dXNlcnMvKi9wZXJzb25hbEFjY2Vzc1Rva2Vucy8qfRKVAQoQTGlzdFVzZXJXZWJob29rcxIlLm1lbW9zLmFwaS52MS5MaXN0VXNlcldlYmhvb2tzUmVxdWVzdBomLm1lbW9zLmFwaS52MS5MaXN0VXNlcldlYmhvb2tzUmVzcG9uc2UiMtpBBnBhcmVudILT5JMCIxIhL2FwaS92MS97cGFyZW50PXVzZXJzLyp9L3dlYmhvb2tzEpsBChFDcmVhdGVVc2VyV2ViaG9vaxImLm1lbW9zLmFwaS52MS5DcmVhdGVVc2VyV2ViaG9va1JlcXVlc3QaGS5tZW1vcy5hcGkudjEuVXNlcldlYmhvb2siQ9pBDnBhcmVudCx3ZWJob29rgtPkkwIsOgd3ZWJob29rIiEvYXBpL3YxL3twYXJlbnQ9dXNlcnMvKn0vd2ViaG9va3MSqAEKEVVwZGF0ZVVzZXJXZWJob29rEiYubWVtb3MuYXBpLnYxLlVwZGF0ZVVzZXJXZWJob29rUmVxdWVzdBoZLm1lbW9zLmFwaS52MS5Vc2VyV2ViaG9vayJQ2kETd2ViaG9vayx1cGRhdGVfbWFza4LT5JMCNDoHd2ViaG9vazIpL2FwaS92MS97d2ViaG9vay5uYW1lPXVzZXJzLyovd2ViaG9va3MvKn0ShQEKEURlbGV0ZVVzZXJXZWJob29rEiYubWVtb3MuYXBpLnYxLkRlbGV0ZVVzZXJXZWJob29rUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSIw2kEEbmFtZYLT5JMCIyohL2FwaS92MS97bmFtZT11c2Vycy8qL3dlYmhvb2tzLyp9EqkBChVMaXN0VXNlck5vdGlmaWNhdGlvbnMSKi5tZW1vcy5hcGkudjEuTGlzdFVzZXJOb3RpZmljYXRpb25zUmVxdWVzdBorLm1lbW9zLmFwaS52MS5MaXN0VXNlck5vdGlmaWNhdGlvbnNSZXNwb25zZSI32kEGcGFyZW50gtPkkwIoEiYvYXBpL3YxL3twYXJlbnQ9dXNlcnMvKn0vbm90aWZpY2F0aW9ucxLLAQoWVXBkYXRlVXNlck5vdGlmaWNhdGlvbhIrLm1lbW9zLmFwaS52MS5VcGRhdGVVc2VyTm90aWZpY2F0aW9uUmVxdWVzdBoeLm1lbW9zLmFwaS52MS5Vc2VyTm90aWZpY2F0aW9uImTaQRhub3RpZmljYXRpb24sdXBkYXRlX21hc2uC0+STAkM6DG5vdGlmaWNhdGlvbjIzL2FwaS92MS97bm90aWZpY2F0aW9uLm5hbWU9dXNlcnMvKi9ub3RpZmljYXRpb25zLyp9EpQBChZEZWxldGVVc2VyTm90aWZpY2F0aW9uEisubWVtb3MuYXBpLnYxLkRlbGV0ZVVzZXJOb3RpZmljYXRpb25SZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IjXaQQRuYW1lgtPkkwIoKiYvYXBpL3YxL3tuYW1lPXVzZXJzLyovbm90aWZpY2F0aW9ucy8qfUKoAQoQY29tLm1lbW9zLmFwaS52MUIQVXNlclNlcnZpY2VQcm90b1ABWjBnaXRodWIuY29tL3VzZW1lbW9zL21lbW9zL3Byb3RvL2dlbi9hcGkvdjE7YXBpdjGiAgNNQViqAgxNZW1vcy5BcGkuVjHKAgxNZW1vc1xBcGlcVjHiAhhNZW1vc1xBcGlcVjFcR1BCTWV0YWRhdGHqAg5NZW1vczo6QXBpOjpWMWIGcHJvdG8z", [file_api_v1_common, file_google_api_annotations, file_google_api_client, file_google_api_field_behavior, file_google_api_resource, file_google_protobuf_empty, file_google_protobuf_field_mask, file_google_protobuf_timestamp]); /** * @generated from message memos.api.v1.User @@ -1157,11 +1157,15 @@ export type UserNotification = Message<"memos.api.v1.UserNotification"> & { type: UserNotification_Type; /** - * The activity ID associated with this notification. - * - * @generated from field: optional int32 activity_id = 6; + * @generated from oneof memos.api.v1.UserNotification.payload */ - activityId?: number; + payload: { + /** + * @generated from field: memos.api.v1.UserNotification.MemoCommentPayload memo_comment = 6; + */ + value: UserNotification_MemoCommentPayload; + case: "memoComment"; + } | { case: undefined; value?: undefined }; }; /** @@ -1171,6 +1175,34 @@ export type UserNotification = Message<"memos.api.v1.UserNotification"> & { export const UserNotificationSchema: GenMessage = /*@__PURE__*/ messageDesc(file_api_v1_user_service, 28); +/** + * @generated from message memos.api.v1.UserNotification.MemoCommentPayload + */ +export type UserNotification_MemoCommentPayload = Message<"memos.api.v1.UserNotification.MemoCommentPayload"> & { + /** + * The memo name of comment. + * Format: memos/{memo} + * + * @generated from field: string memo = 1; + */ + memo: string; + + /** + * The name of related memo. + * Format: memos/{memo} + * + * @generated from field: string related_memo = 2; + */ + relatedMemo: string; +}; + +/** + * Describes the message memos.api.v1.UserNotification.MemoCommentPayload. + * Use `create(UserNotification_MemoCommentPayloadSchema)` to create a new message. + */ +export const UserNotification_MemoCommentPayloadSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_api_v1_user_service, 28, 0); + /** * @generated from enum memos.api.v1.UserNotification.Status */