From 8948edf65405b2379f6841706b320b1b21463f04 Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 13 May 2024 20:24:11 +0800 Subject: [PATCH] chore: impl memo payload definition --- proto/gen/store/memo.pb.go | 237 +++++++++++++++++++++++++++++++++++++ proto/store/memo.proto | 16 +++ store/db/mysql/memo.go | 28 ++++- store/db/postgres/memo.go | 31 ++++- store/db/sqlite/memo.go | 32 ++++- store/memo.go | 4 + 6 files changed, 339 insertions(+), 9 deletions(-) create mode 100644 proto/gen/store/memo.pb.go create mode 100644 proto/store/memo.proto diff --git a/proto/gen/store/memo.pb.go b/proto/gen/store/memo.pb.go new file mode 100644 index 00000000..1be68115 --- /dev/null +++ b/proto/gen/store/memo.pb.go @@ -0,0 +1,237 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc (unknown) +// source: store/memo.proto + +package store + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +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) +) + +type MemoPayload struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // property is the memo's property. + Property *MemoPayload_Property `protobuf:"bytes,1,opt,name=property,proto3" json:"property,omitempty"` +} + +func (x *MemoPayload) Reset() { + *x = MemoPayload{} + if protoimpl.UnsafeEnabled { + mi := &file_store_memo_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MemoPayload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MemoPayload) ProtoMessage() {} + +func (x *MemoPayload) ProtoReflect() protoreflect.Message { + mi := &file_store_memo_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MemoPayload.ProtoReflect.Descriptor instead. +func (*MemoPayload) Descriptor() ([]byte, []int) { + return file_store_memo_proto_rawDescGZIP(), []int{0} +} + +func (x *MemoPayload) GetProperty() *MemoPayload_Property { + if x != nil { + return x.Property + } + return nil +} + +type MemoPayload_Property struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Tags []string `protobuf:"bytes,1,rep,name=tags,proto3" json:"tags,omitempty"` + HasLink bool `protobuf:"varint,2,opt,name=has_link,json=hasLink,proto3" json:"has_link,omitempty"` + HasTodo bool `protobuf:"varint,3,opt,name=has_todo,json=hasTodo,proto3" json:"has_todo,omitempty"` +} + +func (x *MemoPayload_Property) Reset() { + *x = MemoPayload_Property{} + if protoimpl.UnsafeEnabled { + mi := &file_store_memo_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MemoPayload_Property) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MemoPayload_Property) ProtoMessage() {} + +func (x *MemoPayload_Property) ProtoReflect() protoreflect.Message { + mi := &file_store_memo_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MemoPayload_Property.ProtoReflect.Descriptor instead. +func (*MemoPayload_Property) Descriptor() ([]byte, []int) { + return file_store_memo_proto_rawDescGZIP(), []int{0, 0} +} + +func (x *MemoPayload_Property) GetTags() []string { + if x != nil { + return x.Tags + } + return nil +} + +func (x *MemoPayload_Property) GetHasLink() bool { + if x != nil { + return x.HasLink + } + return false +} + +func (x *MemoPayload_Property) GetHasTodo() bool { + if x != nil { + return x.HasTodo + } + return false +} + +var File_store_memo_proto protoreflect.FileDescriptor + +var file_store_memo_proto_rawDesc = []byte{ + 0x0a, 0x10, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x0b, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x22, + 0xa2, 0x01, 0x0a, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, + 0x3d, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, + 0x4d, 0x65, 0x6d, 0x6f, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x70, + 0x65, 0x72, 0x74, 0x79, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x1a, 0x54, + 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, + 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x19, + 0x0a, 0x08, 0x68, 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x07, 0x68, 0x61, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x68, 0x61, 0x73, + 0x5f, 0x74, 0x6f, 0x64, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x68, 0x61, 0x73, + 0x54, 0x6f, 0x64, 0x6f, 0x42, 0x94, 0x01, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x65, 0x6d, + 0x6f, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x09, 0x4d, 0x65, 0x6d, 0x6f, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x75, 0x73, 0x65, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0xa2, 0x02, 0x03, 0x4d, 0x53, 0x58, 0xaa, 0x02, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x53, + 0x74, 0x6f, 0x72, 0x65, 0xca, 0x02, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x53, 0x74, 0x6f, + 0x72, 0x65, 0xe2, 0x02, 0x17, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x53, 0x74, 0x6f, 0x72, 0x65, + 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0c, 0x4d, + 0x65, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_store_memo_proto_rawDescOnce sync.Once + file_store_memo_proto_rawDescData = file_store_memo_proto_rawDesc +) + +func file_store_memo_proto_rawDescGZIP() []byte { + file_store_memo_proto_rawDescOnce.Do(func() { + file_store_memo_proto_rawDescData = protoimpl.X.CompressGZIP(file_store_memo_proto_rawDescData) + }) + return file_store_memo_proto_rawDescData +} + +var file_store_memo_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_store_memo_proto_goTypes = []interface{}{ + (*MemoPayload)(nil), // 0: memos.store.MemoPayload + (*MemoPayload_Property)(nil), // 1: memos.store.MemoPayload.Property +} +var file_store_memo_proto_depIdxs = []int32{ + 1, // 0: memos.store.MemoPayload.property:type_name -> memos.store.MemoPayload.Property + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_store_memo_proto_init() } +func file_store_memo_proto_init() { + if File_store_memo_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_store_memo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MemoPayload); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_store_memo_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MemoPayload_Property); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_store_memo_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_store_memo_proto_goTypes, + DependencyIndexes: file_store_memo_proto_depIdxs, + MessageInfos: file_store_memo_proto_msgTypes, + }.Build() + File_store_memo_proto = out.File + file_store_memo_proto_rawDesc = nil + file_store_memo_proto_goTypes = nil + file_store_memo_proto_depIdxs = nil +} diff --git a/proto/store/memo.proto b/proto/store/memo.proto new file mode 100644 index 00000000..3a89d601 --- /dev/null +++ b/proto/store/memo.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package memos.store; + +option go_package = "gen/store"; + +message MemoPayload { + // property is the memo's property. + Property property = 1; + + message Property { + repeated string tags = 1; + bool has_link = 2; + bool has_todo = 3; + } +} diff --git a/store/db/mysql/memo.go b/store/db/mysql/memo.go index 77d9ea61..0a321780 100644 --- a/store/db/mysql/memo.go +++ b/store/db/mysql/memo.go @@ -7,7 +7,9 @@ import ( "strings" "github.com/pkg/errors" + "google.golang.org/protobuf/encoding/protojson" + storepb "github.com/usememos/memos/proto/gen/store" "github.com/usememos/memos/store" ) @@ -22,7 +24,15 @@ func (d *DB) CreateMemo(ctx context.Context, create *store.Memo) (*store.Memo, e } tags = string(tagsBytes) } - args := []any{create.UID, create.CreatorID, create.Content, create.Visibility, tags, "{}"} + payload := "{}" + if create.Payload != nil { + payloadBytes, err := protojson.Marshal(create.Payload) + if err != nil { + return nil, err + } + payload = string(payloadBytes) + } + args := []any{create.UID, create.CreatorID, create.Content, create.Visibility, tags, payload} stmt := "INSERT INTO `memo` (" + strings.Join(fields, ", ") + ") VALUES (" + strings.Join(placeholder, ", ") + ")" result, err := d.db.ExecContext(ctx, stmt, args...) @@ -115,6 +125,7 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo "`memo`.`row_status` AS `row_status`", "`memo`.`visibility` AS `visibility`", "`memo`.`tags` AS `tags`", + "`memo`.`payload` AS `payload`", "IFNULL(`memo_organizer`.`pinned`, 0) AS `pinned`", "`memo_relation`.`related_memo_id` AS `parent_id`", } @@ -139,7 +150,7 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo list := make([]*store.Memo, 0) for rows.Next() { var memo store.Memo - var tagsBytes []byte + var tagsBytes, payloadBytes []byte dests := []any{ &memo.ID, &memo.UID, @@ -149,6 +160,7 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo &memo.RowStatus, &memo.Visibility, &tagsBytes, + &payloadBytes, &memo.Pinned, &memo.ParentID, } @@ -161,6 +173,11 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo if err := json.Unmarshal(tagsBytes, &memo.Tags); err != nil { return nil, errors.Wrap(err, "failed to unmarshal tags") } + payload := &storepb.MemoPayload{} + if err := protojsonUnmarshaler.Unmarshal(payloadBytes, payload); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal payload") + } + memo.Payload = payload list = append(list, &memo) } @@ -211,6 +228,13 @@ func (d *DB) UpdateMemo(ctx context.Context, update *store.UpdateMemo) error { } set, args = append(set, "`tags` = ?"), append(args, string(tagsBytes)) } + if v := update.Payload; v != nil { + payloadBytes, err := protojson.Marshal(v) + if err != nil { + return err + } + set, args = append(set, "`payload` = ?"), append(args, string(payloadBytes)) + } args = append(args, update.ID) stmt := "UPDATE `memo` SET " + strings.Join(set, ", ") + " WHERE `id` = ?" diff --git a/store/db/postgres/memo.go b/store/db/postgres/memo.go index 32a9adfe..38965f19 100644 --- a/store/db/postgres/memo.go +++ b/store/db/postgres/memo.go @@ -7,12 +7,14 @@ import ( "strings" "github.com/pkg/errors" + "google.golang.org/protobuf/encoding/protojson" + storepb "github.com/usememos/memos/proto/gen/store" "github.com/usememos/memos/store" ) func (d *DB) CreateMemo(ctx context.Context, create *store.Memo) (*store.Memo, error) { - fields := []string{"uid", "creator_id", "content", "visibility", "tags"} + fields := []string{"uid", "creator_id", "content", "visibility", "tags", "payload"} tags := "[]" if len(create.Tags) != 0 { tagsBytes, err := json.Marshal(create.Tags) @@ -21,7 +23,15 @@ func (d *DB) CreateMemo(ctx context.Context, create *store.Memo) (*store.Memo, e } tags = string(tagsBytes) } - args := []any{create.UID, create.CreatorID, create.Content, create.Visibility, tags} + payload := "{}" + if create.Payload != nil { + payloadBytes, err := protojson.Marshal(create.Payload) + if err != nil { + return nil, err + } + payload = string(payloadBytes) + } + args := []any{create.UID, create.CreatorID, create.Content, create.Visibility, tags, payload} stmt := "INSERT INTO memo (" + strings.Join(fields, ", ") + ") VALUES (" + placeholders(len(args)) + ") RETURNING id, created_ts, updated_ts, row_status" if err := d.db.QueryRowContext(ctx, stmt, args...).Scan( @@ -107,6 +117,7 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo `memo.row_status AS row_status`, `memo.visibility AS visibility`, `memo.tags AS tags`, + `memo.payload AS payload`, `COALESCE(memo_organizer.pinned, 0) AS pinned`, `memo_relation.related_memo_id AS parent_id`, } @@ -136,7 +147,7 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo list := make([]*store.Memo, 0) for rows.Next() { var memo store.Memo - var tagsBytes []byte + var tagsBytes, payloadBytes []byte dests := []any{ &memo.ID, &memo.UID, @@ -146,6 +157,7 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo &memo.RowStatus, &memo.Visibility, &tagsBytes, + &payloadBytes, &memo.Pinned, &memo.ParentID, } @@ -158,6 +170,11 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo if err := json.Unmarshal(tagsBytes, &memo.Tags); err != nil { return nil, errors.Wrap(err, "failed to unmarshal tags") } + payload := &storepb.MemoPayload{} + if err := protojsonUnmarshaler.Unmarshal(payloadBytes, payload); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal payload") + } + memo.Payload = payload list = append(list, &memo) } @@ -208,6 +225,14 @@ func (d *DB) UpdateMemo(ctx context.Context, update *store.UpdateMemo) error { } set, args = append(set, "tags = "+placeholder(len(args)+1)), append(args, string(tagsBytes)) } + if v := update.Payload; v != nil { + payloadBytes, err := protojson.Marshal(v) + if err != nil { + return err + } + set, args = append(set, "payload = "+placeholder(len(args)+1)), append(args, string(payloadBytes)) + } + stmt := `UPDATE memo SET ` + strings.Join(set, ", ") + ` WHERE id = ` + placeholder(len(args)+1) args = append(args, update.ID) if _, err := d.db.ExecContext(ctx, stmt, args...); err != nil { diff --git a/store/db/sqlite/memo.go b/store/db/sqlite/memo.go index 099373a7..9bedbf03 100644 --- a/store/db/sqlite/memo.go +++ b/store/db/sqlite/memo.go @@ -7,13 +7,15 @@ import ( "strings" "github.com/pkg/errors" + "google.golang.org/protobuf/encoding/protojson" + storepb "github.com/usememos/memos/proto/gen/store" "github.com/usememos/memos/store" ) func (d *DB) CreateMemo(ctx context.Context, create *store.Memo) (*store.Memo, error) { - fields := []string{"`uid`", "`creator_id`", "`content`", "`visibility`, `tags`"} - placeholder := []string{"?", "?", "?", "?", "?"} + fields := []string{"`uid`", "`creator_id`", "`content`", "`visibility`", "`tags`", "`payload`"} + placeholder := []string{"?", "?", "?", "?", "?", "?"} tags := "[]" if len(create.Tags) != 0 { tagsBytes, err := json.Marshal(create.Tags) @@ -22,7 +24,15 @@ func (d *DB) CreateMemo(ctx context.Context, create *store.Memo) (*store.Memo, e } tags = string(tagsBytes) } - args := []any{create.UID, create.CreatorID, create.Content, create.Visibility, tags} + payload := "{}" + if create.Payload != nil { + payloadBytes, err := protojson.Marshal(create.Payload) + if err != nil { + return nil, err + } + payload = string(payloadBytes) + } + args := []any{create.UID, create.CreatorID, create.Content, create.Visibility, tags, payload} stmt := "INSERT INTO `memo` (" + strings.Join(fields, ", ") + ") VALUES (" + strings.Join(placeholder, ", ") + ") RETURNING `id`, `created_ts`, `updated_ts`, `row_status`" if err := d.db.QueryRowContext(ctx, stmt, args...).Scan( @@ -107,6 +117,7 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo "`memo`.`row_status` AS `row_status`", "`memo`.`visibility` AS `visibility`", "`memo`.`tags` AS `tags`", + "`memo`.`payload` AS `payload`", "IFNULL(`memo_organizer`.`pinned`, 0) AS `pinned`", "`memo_relation`.`related_memo_id` AS `parent_id`", } @@ -135,7 +146,7 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo list := make([]*store.Memo, 0) for rows.Next() { var memo store.Memo - var tagsBytes []byte + var tagsBytes, payloadBytes []byte dests := []any{ &memo.ID, &memo.UID, @@ -145,6 +156,7 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo &memo.RowStatus, &memo.Visibility, &tagsBytes, + &payloadBytes, &memo.Pinned, &memo.ParentID, } @@ -157,6 +169,11 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo if err := json.Unmarshal(tagsBytes, &memo.Tags); err != nil { return nil, errors.Wrap(err, "failed to unmarshal tags") } + payload := &storepb.MemoPayload{} + if err := protojsonUnmarshaler.Unmarshal(payloadBytes, payload); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal payload") + } + memo.Payload = payload list = append(list, &memo) } @@ -194,6 +211,13 @@ func (d *DB) UpdateMemo(ctx context.Context, update *store.UpdateMemo) error { } set, args = append(set, "`tags` = ?"), append(args, string(tagsBytes)) } + if v := update.Payload; v != nil { + payloadBytes, err := protojson.Marshal(v) + if err != nil { + return err + } + set, args = append(set, "`payload` = ?"), append(args, string(payloadBytes)) + } args = append(args, update.ID) stmt := "UPDATE `memo` SET " + strings.Join(set, ", ") + " WHERE `id` = ?" diff --git a/store/memo.go b/store/memo.go index 4d85c066..4a535f1a 100644 --- a/store/memo.go +++ b/store/memo.go @@ -5,6 +5,8 @@ import ( "errors" "github.com/usememos/memos/internal/util" + + storepb "github.com/usememos/memos/proto/gen/store" ) // Visibility is the type of a visibility. @@ -47,6 +49,7 @@ type Memo struct { Content string Visibility Visibility Tags []string + Payload *storepb.MemoPayload // Composed fields Pinned bool @@ -89,6 +92,7 @@ type UpdateMemo struct { Content *string Visibility *Visibility Tags *[]string + Payload *storepb.MemoPayload } type DeleteMemo struct {