From fcf5981b979a81b06ad934772ac3092ff55061ca Mon Sep 17 00:00:00 2001 From: Steven Date: Wed, 2 Aug 2023 21:07:38 +0800 Subject: [PATCH] feat: migrate part of shortcut store to v1 --- api/v1/shortcut.go | 30 ++- api/v2/common.go | 2 +- proto/api/v2/common.proto | 2 +- proto/gen/api/v2/README.md | 2 +- proto/gen/api/v2/common.pb.go | 8 +- proto/gen/store/README.md | 140 +++++++++++ proto/gen/store/common.pb.go | 141 +++++++++++ proto/gen/store/shortcut.pb.go | 411 +++++++++++++++++++++++++++++++++ proto/store/common.proto | 13 ++ proto/store/shortcut.proto | 51 ++++ store/common.go | 14 ++ store/shortcut.go | 139 +++++++++++ test/store/shortcut_test.go | 28 +-- test/store/user_test.go | 2 +- 14 files changed, 957 insertions(+), 26 deletions(-) create mode 100644 proto/gen/store/README.md create mode 100644 proto/gen/store/common.pb.go create mode 100644 proto/gen/store/shortcut.pb.go create mode 100644 proto/store/common.proto create mode 100644 proto/store/shortcut.proto diff --git a/api/v1/shortcut.go b/api/v1/shortcut.go index 7267e11..eeb1c56 100644 --- a/api/v1/shortcut.go +++ b/api/v1/shortcut.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/boojack/slash/internal/util" + storepb "github.com/boojack/slash/proto/gen/store" "github.com/boojack/slash/store" "github.com/labstack/echo/v4" "github.com/pkg/errors" @@ -205,9 +206,9 @@ func (s *APIV1Service) registerShortcutRoutes(g *echo.Group) { find.Tag = &tag } - list := []*store.Shortcut{} + list := []*storepb.Shortcut{} find.VisibilityList = []store.Visibility{store.VisibilityWorkspace, store.VisibilityPublic} - visibleShortcutList, err := s.Store.ListShortcuts(ctx, find) + visibleShortcutList, err := s.Store.ListShortcutsV1(ctx, find) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to fetch shortcut list, err: %s", err)).SetInternal(err) } @@ -215,7 +216,7 @@ func (s *APIV1Service) registerShortcutRoutes(g *echo.Group) { find.VisibilityList = []store.Visibility{store.VisibilityPrivate} find.CreatorID = &userID - privateShortcutList, err := s.Store.ListShortcuts(ctx, find) + privateShortcutList, err := s.Store.ListShortcutsV1(ctx, find) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to fetch private shortcut list, err: %s", err)).SetInternal(err) } @@ -223,7 +224,7 @@ func (s *APIV1Service) registerShortcutRoutes(g *echo.Group) { shortcutMessageList := []*Shortcut{} for _, shortcut := range list { - shortcutMessage, err := s.composeShortcut(ctx, convertShortcutFromStore(shortcut)) + shortcutMessage, err := s.composeShortcut(ctx, convertShortcutFromStorepb(shortcut)) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to compose shortcut, err: %s", err)).SetInternal(err) } @@ -349,6 +350,27 @@ func convertShortcutFromStore(shortcut *store.Shortcut) *Shortcut { } } +func convertShortcutFromStorepb(shortcut *storepb.Shortcut) *Shortcut { + return &Shortcut{ + ID: shortcut.Id, + CreatedTs: shortcut.CreatedTs, + UpdatedTs: shortcut.UpdatedTs, + CreatorID: shortcut.CreatorId, + RowStatus: RowStatus(shortcut.RowStatus.String()), + Name: shortcut.Name, + Link: shortcut.Link, + Title: shortcut.Title, + Description: shortcut.Description, + Visibility: Visibility(shortcut.Visibility.String()), + Tags: shortcut.Tags, + OpenGraphMetadata: &OpenGraphMetadata{ + Title: shortcut.OgMetadata.Title, + Description: shortcut.OgMetadata.Description, + Image: shortcut.OgMetadata.Image, + }, + } +} + func (s *APIV1Service) createShortcutCreateActivity(ctx context.Context, shortcut *store.Shortcut) error { payload := &ActivityShorcutCreatePayload{ ShortcutID: shortcut.ID, diff --git a/api/v2/common.go b/api/v2/common.go index 6167550..bfbd327 100644 --- a/api/v2/common.go +++ b/api/v2/common.go @@ -8,7 +8,7 @@ import ( func convertRowStatusFromStore(rowStatus store.RowStatus) apiv2pb.RowStatus { switch rowStatus { case store.Normal: - return apiv2pb.RowStatus_ACTIVE + return apiv2pb.RowStatus_NORMAL case store.Archived: return apiv2pb.RowStatus_ARCHIVED default: diff --git a/proto/api/v2/common.proto b/proto/api/v2/common.proto index 519d081..df62c7f 100644 --- a/proto/api/v2/common.proto +++ b/proto/api/v2/common.proto @@ -7,7 +7,7 @@ option go_package = "gen/api/v2"; enum RowStatus { ROW_STATUS_UNSPECIFIED = 0; - ACTIVE = 1; + NORMAL = 1; ARCHIVED = 2; } diff --git a/proto/gen/api/v2/README.md b/proto/gen/api/v2/README.md index 92d650c..ffb7325 100644 --- a/proto/gen/api/v2/README.md +++ b/proto/gen/api/v2/README.md @@ -36,7 +36,7 @@ | Name | Number | Description | | ---- | ------ | ----------- | | ROW_STATUS_UNSPECIFIED | 0 | | -| ACTIVE | 1 | | +| NORMAL | 1 | | | ARCHIVED | 2 | | diff --git a/proto/gen/api/v2/common.pb.go b/proto/gen/api/v2/common.pb.go index 7308079..4418d14 100644 --- a/proto/gen/api/v2/common.pb.go +++ b/proto/gen/api/v2/common.pb.go @@ -24,7 +24,7 @@ type RowStatus int32 const ( RowStatus_ROW_STATUS_UNSPECIFIED RowStatus = 0 - RowStatus_ACTIVE RowStatus = 1 + RowStatus_NORMAL RowStatus = 1 RowStatus_ARCHIVED RowStatus = 2 ) @@ -32,12 +32,12 @@ const ( var ( RowStatus_name = map[int32]string{ 0: "ROW_STATUS_UNSPECIFIED", - 1: "ACTIVE", + 1: "NORMAL", 2: "ARCHIVED", } RowStatus_value = map[string]int32{ "ROW_STATUS_UNSPECIFIED": 0, - "ACTIVE": 1, + "NORMAL": 1, "ARCHIVED": 2, } ) @@ -77,7 +77,7 @@ var file_api_v2_common_proto_rawDesc = []byte{ 0x2e, 0x76, 0x32, 0x2a, 0x41, 0x0a, 0x09, 0x52, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x16, 0x52, 0x4f, 0x57, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, - 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x52, 0x43, 0x48, + 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x52, 0x43, 0x48, 0x49, 0x56, 0x45, 0x44, 0x10, 0x02, 0x42, 0xa2, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x42, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, diff --git a/proto/gen/store/README.md b/proto/gen/store/README.md new file mode 100644 index 0000000..348a862 --- /dev/null +++ b/proto/gen/store/README.md @@ -0,0 +1,140 @@ +# Protocol Documentation + + +## Table of Contents + +- [store/common.proto](#store_common-proto) + - [RowStatus](#slash-store-RowStatus) + +- [store/shortcut.proto](#store_shortcut-proto) + - [OpenGraphMetadata](#slash-store-OpenGraphMetadata) + - [Shortcut](#slash-store-Shortcut) + + - [Visibility](#slash-store-Visibility) + +- [Scalar Value Types](#scalar-value-types) + + + + +

Top

+ +## store/common.proto + + + + + + + +### RowStatus + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| ROW_STATUS_UNSPECIFIED | 0 | | +| NORMAL | 1 | | +| ARCHIVED | 2 | | + + + + + + + + + + + +

Top

+ +## store/shortcut.proto + + + + + +### OpenGraphMetadata + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| title | [string](#string) | | | +| description | [string](#string) | | | +| image | [string](#string) | | | + + + + + + + + +### Shortcut + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| id | [int32](#int32) | | | +| creator_id | [int32](#int32) | | | +| created_ts | [int64](#int64) | | | +| updated_ts | [int64](#int64) | | | +| row_status | [RowStatus](#slash-store-RowStatus) | | | +| name | [string](#string) | | | +| link | [string](#string) | | | +| title | [string](#string) | | | +| tags | [string](#string) | repeated | | +| description | [string](#string) | | | +| visibility | [Visibility](#slash-store-Visibility) | | | +| og_metadata | [OpenGraphMetadata](#slash-store-OpenGraphMetadata) | | | + + + + + + + + + + +### Visibility + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| VISIBILITY_UNSPECIFIED | 0 | | +| PRIVATE | 1 | | +| WORKSPACE | 2 | | +| PUBLIC | 3 | | + + + + + + + + + + +## Scalar Value Types + +| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | +| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | +| double | | double | double | float | float64 | double | float | Float | +| float | | float | float | float | float32 | float | float | Float | +| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) | +| uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) | +| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) | +| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum | +| sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| bool | | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | +| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) | +| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) | + diff --git a/proto/gen/store/common.pb.go b/proto/gen/store/common.pb.go new file mode 100644 index 0000000..66f7a95 --- /dev/null +++ b/proto/gen/store/common.pb.go @@ -0,0 +1,141 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc (unknown) +// source: store/common.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 RowStatus int32 + +const ( + RowStatus_ROW_STATUS_UNSPECIFIED RowStatus = 0 + RowStatus_NORMAL RowStatus = 1 + RowStatus_ARCHIVED RowStatus = 2 +) + +// Enum value maps for RowStatus. +var ( + RowStatus_name = map[int32]string{ + 0: "ROW_STATUS_UNSPECIFIED", + 1: "NORMAL", + 2: "ARCHIVED", + } + RowStatus_value = map[string]int32{ + "ROW_STATUS_UNSPECIFIED": 0, + "NORMAL": 1, + "ARCHIVED": 2, + } +) + +func (x RowStatus) Enum() *RowStatus { + p := new(RowStatus) + *p = x + return p +} + +func (x RowStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (RowStatus) Descriptor() protoreflect.EnumDescriptor { + return file_store_common_proto_enumTypes[0].Descriptor() +} + +func (RowStatus) Type() protoreflect.EnumType { + return &file_store_common_proto_enumTypes[0] +} + +func (x RowStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use RowStatus.Descriptor instead. +func (RowStatus) EnumDescriptor() ([]byte, []int) { + return file_store_common_proto_rawDescGZIP(), []int{0} +} + +var File_store_common_proto protoreflect.FileDescriptor + +var file_store_common_proto_rawDesc = []byte{ + 0x0a, 0x12, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x2a, 0x41, 0x0a, 0x09, 0x52, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, + 0x0a, 0x16, 0x52, 0x4f, 0x57, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, + 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x4f, + 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x52, 0x43, 0x48, 0x49, 0x56, + 0x45, 0x44, 0x10, 0x02, 0x42, 0x95, 0x01, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x6c, 0x61, + 0x73, 0x68, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6f, 0x6f, 0x6a, 0x61, 0x63, 0x6b, 0x2f, 0x73, 0x6c, 0x61, 0x73, + 0x68, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0xa2, 0x02, 0x03, 0x53, 0x53, 0x58, 0xaa, 0x02, 0x0b, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x2e, + 0x53, 0x74, 0x6f, 0x72, 0x65, 0xca, 0x02, 0x0b, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x5c, 0x53, 0x74, + 0x6f, 0x72, 0x65, 0xe2, 0x02, 0x17, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x5c, 0x53, 0x74, 0x6f, 0x72, + 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0c, + 0x53, 0x6c, 0x61, 0x73, 0x68, 0x3a, 0x3a, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_store_common_proto_rawDescOnce sync.Once + file_store_common_proto_rawDescData = file_store_common_proto_rawDesc +) + +func file_store_common_proto_rawDescGZIP() []byte { + file_store_common_proto_rawDescOnce.Do(func() { + file_store_common_proto_rawDescData = protoimpl.X.CompressGZIP(file_store_common_proto_rawDescData) + }) + return file_store_common_proto_rawDescData +} + +var file_store_common_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_store_common_proto_goTypes = []interface{}{ + (RowStatus)(0), // 0: slash.store.RowStatus +} +var file_store_common_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_store_common_proto_init() } +func file_store_common_proto_init() { + if File_store_common_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_store_common_proto_rawDesc, + NumEnums: 1, + NumMessages: 0, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_store_common_proto_goTypes, + DependencyIndexes: file_store_common_proto_depIdxs, + EnumInfos: file_store_common_proto_enumTypes, + }.Build() + File_store_common_proto = out.File + file_store_common_proto_rawDesc = nil + file_store_common_proto_goTypes = nil + file_store_common_proto_depIdxs = nil +} diff --git a/proto/gen/store/shortcut.pb.go b/proto/gen/store/shortcut.pb.go new file mode 100644 index 0000000..c281608 --- /dev/null +++ b/proto/gen/store/shortcut.pb.go @@ -0,0 +1,411 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc (unknown) +// source: store/shortcut.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 Visibility int32 + +const ( + Visibility_VISIBILITY_UNSPECIFIED Visibility = 0 + Visibility_PRIVATE Visibility = 1 + Visibility_WORKSPACE Visibility = 2 + Visibility_PUBLIC Visibility = 3 +) + +// Enum value maps for Visibility. +var ( + Visibility_name = map[int32]string{ + 0: "VISIBILITY_UNSPECIFIED", + 1: "PRIVATE", + 2: "WORKSPACE", + 3: "PUBLIC", + } + Visibility_value = map[string]int32{ + "VISIBILITY_UNSPECIFIED": 0, + "PRIVATE": 1, + "WORKSPACE": 2, + "PUBLIC": 3, + } +) + +func (x Visibility) Enum() *Visibility { + p := new(Visibility) + *p = x + return p +} + +func (x Visibility) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Visibility) Descriptor() protoreflect.EnumDescriptor { + return file_store_shortcut_proto_enumTypes[0].Descriptor() +} + +func (Visibility) Type() protoreflect.EnumType { + return &file_store_shortcut_proto_enumTypes[0] +} + +func (x Visibility) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Visibility.Descriptor instead. +func (Visibility) EnumDescriptor() ([]byte, []int) { + return file_store_shortcut_proto_rawDescGZIP(), []int{0} +} + +type Shortcut struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + CreatorId int32 `protobuf:"varint,2,opt,name=creator_id,json=creatorId,proto3" json:"creator_id,omitempty"` + CreatedTs int64 `protobuf:"varint,3,opt,name=created_ts,json=createdTs,proto3" json:"created_ts,omitempty"` + UpdatedTs int64 `protobuf:"varint,4,opt,name=updated_ts,json=updatedTs,proto3" json:"updated_ts,omitempty"` + RowStatus RowStatus `protobuf:"varint,5,opt,name=row_status,json=rowStatus,proto3,enum=slash.store.RowStatus" json:"row_status,omitempty"` + Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"` + Link string `protobuf:"bytes,7,opt,name=link,proto3" json:"link,omitempty"` + Title string `protobuf:"bytes,8,opt,name=title,proto3" json:"title,omitempty"` + Tags []string `protobuf:"bytes,9,rep,name=tags,proto3" json:"tags,omitempty"` + Description string `protobuf:"bytes,10,opt,name=description,proto3" json:"description,omitempty"` + Visibility Visibility `protobuf:"varint,11,opt,name=visibility,proto3,enum=slash.store.Visibility" json:"visibility,omitempty"` + OgMetadata *OpenGraphMetadata `protobuf:"bytes,12,opt,name=og_metadata,json=ogMetadata,proto3" json:"og_metadata,omitempty"` +} + +func (x *Shortcut) Reset() { + *x = Shortcut{} + if protoimpl.UnsafeEnabled { + mi := &file_store_shortcut_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Shortcut) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Shortcut) ProtoMessage() {} + +func (x *Shortcut) ProtoReflect() protoreflect.Message { + mi := &file_store_shortcut_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 Shortcut.ProtoReflect.Descriptor instead. +func (*Shortcut) Descriptor() ([]byte, []int) { + return file_store_shortcut_proto_rawDescGZIP(), []int{0} +} + +func (x *Shortcut) GetId() int32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Shortcut) GetCreatorId() int32 { + if x != nil { + return x.CreatorId + } + return 0 +} + +func (x *Shortcut) GetCreatedTs() int64 { + if x != nil { + return x.CreatedTs + } + return 0 +} + +func (x *Shortcut) GetUpdatedTs() int64 { + if x != nil { + return x.UpdatedTs + } + return 0 +} + +func (x *Shortcut) GetRowStatus() RowStatus { + if x != nil { + return x.RowStatus + } + return RowStatus_ROW_STATUS_UNSPECIFIED +} + +func (x *Shortcut) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Shortcut) GetLink() string { + if x != nil { + return x.Link + } + return "" +} + +func (x *Shortcut) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *Shortcut) GetTags() []string { + if x != nil { + return x.Tags + } + return nil +} + +func (x *Shortcut) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Shortcut) GetVisibility() Visibility { + if x != nil { + return x.Visibility + } + return Visibility_VISIBILITY_UNSPECIFIED +} + +func (x *Shortcut) GetOgMetadata() *OpenGraphMetadata { + if x != nil { + return x.OgMetadata + } + return nil +} + +type OpenGraphMetadata struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + Image string `protobuf:"bytes,3,opt,name=image,proto3" json:"image,omitempty"` +} + +func (x *OpenGraphMetadata) Reset() { + *x = OpenGraphMetadata{} + if protoimpl.UnsafeEnabled { + mi := &file_store_shortcut_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OpenGraphMetadata) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OpenGraphMetadata) ProtoMessage() {} + +func (x *OpenGraphMetadata) ProtoReflect() protoreflect.Message { + mi := &file_store_shortcut_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 OpenGraphMetadata.ProtoReflect.Descriptor instead. +func (*OpenGraphMetadata) Descriptor() ([]byte, []int) { + return file_store_shortcut_proto_rawDescGZIP(), []int{1} +} + +func (x *OpenGraphMetadata) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *OpenGraphMetadata) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *OpenGraphMetadata) GetImage() string { + if x != nil { + return x.Image + } + return "" +} + +var File_store_shortcut_proto protoreflect.FileDescriptor + +var file_store_shortcut_proto_rawDesc = []byte{ + 0x0a, 0x14, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x63, 0x75, 0x74, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x1a, 0x12, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9c, 0x03, 0x0a, 0x08, 0x53, 0x68, 0x6f, 0x72, + 0x74, 0x63, 0x75, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, + 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x54, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x73, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x54, + 0x73, 0x12, 0x35, 0x0a, 0x0a, 0x72, 0x6f, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x2e, 0x52, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x09, 0x72, + 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x6b, + 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x09, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x0a, + 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x17, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x56, + 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, + 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x0b, 0x6f, 0x67, 0x5f, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x73, 0x6c, 0x61, + 0x73, 0x68, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x47, 0x72, 0x61, + 0x70, 0x68, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0a, 0x6f, 0x67, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x61, 0x0a, 0x11, 0x4f, 0x70, 0x65, 0x6e, 0x47, 0x72, + 0x61, 0x70, 0x68, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x74, + 0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, + 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2a, 0x50, 0x0a, 0x0a, 0x56, 0x69, 0x73, + 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x1a, 0x0a, 0x16, 0x56, 0x49, 0x53, 0x49, 0x42, + 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, + 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x10, 0x01, + 0x12, 0x0d, 0x0a, 0x09, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x10, 0x02, 0x12, + 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x03, 0x42, 0x97, 0x01, 0x0a, 0x0f, + 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, + 0x0d, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x63, 0x75, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, + 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6f, 0x6f, + 0x6a, 0x61, 0x63, 0x6b, 0x2f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0xa2, 0x02, 0x03, 0x53, 0x53, 0x58, + 0xaa, 0x02, 0x0b, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0xca, 0x02, + 0x0b, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x5c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0xe2, 0x02, 0x17, 0x53, + 0x6c, 0x61, 0x73, 0x68, 0x5c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0c, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x3a, 0x3a, + 0x53, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_store_shortcut_proto_rawDescOnce sync.Once + file_store_shortcut_proto_rawDescData = file_store_shortcut_proto_rawDesc +) + +func file_store_shortcut_proto_rawDescGZIP() []byte { + file_store_shortcut_proto_rawDescOnce.Do(func() { + file_store_shortcut_proto_rawDescData = protoimpl.X.CompressGZIP(file_store_shortcut_proto_rawDescData) + }) + return file_store_shortcut_proto_rawDescData +} + +var file_store_shortcut_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_store_shortcut_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_store_shortcut_proto_goTypes = []interface{}{ + (Visibility)(0), // 0: slash.store.Visibility + (*Shortcut)(nil), // 1: slash.store.Shortcut + (*OpenGraphMetadata)(nil), // 2: slash.store.OpenGraphMetadata + (RowStatus)(0), // 3: slash.store.RowStatus +} +var file_store_shortcut_proto_depIdxs = []int32{ + 3, // 0: slash.store.Shortcut.row_status:type_name -> slash.store.RowStatus + 0, // 1: slash.store.Shortcut.visibility:type_name -> slash.store.Visibility + 2, // 2: slash.store.Shortcut.og_metadata:type_name -> slash.store.OpenGraphMetadata + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_store_shortcut_proto_init() } +func file_store_shortcut_proto_init() { + if File_store_shortcut_proto != nil { + return + } + file_store_common_proto_init() + if !protoimpl.UnsafeEnabled { + file_store_shortcut_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Shortcut); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_store_shortcut_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OpenGraphMetadata); 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_shortcut_proto_rawDesc, + NumEnums: 1, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_store_shortcut_proto_goTypes, + DependencyIndexes: file_store_shortcut_proto_depIdxs, + EnumInfos: file_store_shortcut_proto_enumTypes, + MessageInfos: file_store_shortcut_proto_msgTypes, + }.Build() + File_store_shortcut_proto = out.File + file_store_shortcut_proto_rawDesc = nil + file_store_shortcut_proto_goTypes = nil + file_store_shortcut_proto_depIdxs = nil +} diff --git a/proto/store/common.proto b/proto/store/common.proto new file mode 100644 index 0000000..d52604e --- /dev/null +++ b/proto/store/common.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +package slash.store; + +option go_package = "gen/store"; + +enum RowStatus { + ROW_STATUS_UNSPECIFIED = 0; + + NORMAL = 1; + + ARCHIVED = 2; +} diff --git a/proto/store/shortcut.proto b/proto/store/shortcut.proto new file mode 100644 index 0000000..7784162 --- /dev/null +++ b/proto/store/shortcut.proto @@ -0,0 +1,51 @@ +syntax = "proto3"; + +package slash.store; + +import "store/common.proto"; + +option go_package = "gen/store"; + +message Shortcut { + int32 id = 1; + + int32 creator_id = 2; + + int64 created_ts = 3; + + int64 updated_ts = 4; + + RowStatus row_status = 5; + + string name = 6; + + string link = 7; + + string title = 8; + + repeated string tags = 9; + + string description = 10; + + Visibility visibility = 11; + + OpenGraphMetadata og_metadata = 12; +} + +message OpenGraphMetadata { + string title = 1; + + string description = 2; + + string image = 3; +} + +enum Visibility { + VISIBILITY_UNSPECIFIED = 0; + + PRIVATE = 1; + + WORKSPACE = 2; + + PUBLIC = 3; +} diff --git a/store/common.go b/store/common.go index c6699de..fcda0d8 100644 --- a/store/common.go +++ b/store/common.go @@ -1,5 +1,9 @@ package store +import ( + storepb "github.com/boojack/slash/proto/gen/store" +) + // RowStatus is the status for a row. type RowStatus string @@ -19,3 +23,13 @@ func (e RowStatus) String() string { } return "" } + +func convertStorepbRowStatus(status string) storepb.RowStatus { + switch status { + case "NORMAL": + return storepb.RowStatus_NORMAL + case "ARCHIVED": + return storepb.RowStatus_ARCHIVED + } + return storepb.RowStatus_ROW_STATUS_UNSPECIFIED +} diff --git a/store/shortcut.go b/store/shortcut.go index 76a7415..f2f935c 100644 --- a/store/shortcut.go +++ b/store/shortcut.go @@ -6,6 +6,9 @@ import ( "encoding/json" "fmt" "strings" + + storepb "github.com/boojack/slash/proto/gen/store" + "google.golang.org/protobuf/encoding/protojson" ) // Visibility is the type of a visibility. @@ -116,6 +119,41 @@ func (s *Store) CreateShortcut(ctx context.Context, create *Shortcut) (*Shortcut return create, nil } +func (s *Store) CreateShortcutV1(ctx context.Context, create *storepb.Shortcut) (*storepb.Shortcut, error) { + set := []string{"creator_id", "name", "link", "title", "description", "visibility", "tag"} + args := []any{create.CreatorId, create.Name, create.Link, create.Title, create.Description, create.Visibility.String(), strings.Join(create.Tags, " ")} + placeholder := []string{"?", "?", "?", "?", "?", "?", "?"} + if create.OgMetadata != nil { + set = append(set, "og_metadata") + openGraphMetadataBytes, err := protojson.Marshal(create.OgMetadata) + if err != nil { + return nil, err + } + args = append(args, string(openGraphMetadataBytes)) + placeholder = append(placeholder, "?") + } + + stmt := ` + INSERT INTO shortcut ( + ` + strings.Join(set, ", ") + ` + ) + VALUES (` + strings.Join(placeholder, ",") + `) + RETURNING id, created_ts, updated_ts, row_status + ` + var rowStatus string + if err := s.db.QueryRowContext(ctx, stmt, args...).Scan( + &create.Id, + &create.CreatedTs, + &create.UpdatedTs, + &rowStatus, + ); err != nil { + return nil, err + } + create.RowStatus = convertStorepbRowStatus(rowStatus) + + return create, nil +} + func (s *Store) UpdateShortcut(ctx context.Context, update *UpdateShortcut) (*Shortcut, error) { set, args := []string{}, []any{} if update.RowStatus != nil { @@ -277,6 +315,93 @@ func (s *Store) ListShortcuts(ctx context.Context, find *FindShortcut) ([]*Short return list, nil } +func (s *Store) ListShortcutsV1(ctx context.Context, find *FindShortcut) ([]*storepb.Shortcut, error) { + where, args := []string{"1 = 1"}, []any{} + if v := find.ID; v != nil { + where, args = append(where, "id = ?"), append(args, *v) + } + if v := find.CreatorID; v != nil { + where, args = append(where, "creator_id = ?"), append(args, *v) + } + if v := find.RowStatus; v != nil { + where, args = append(where, "row_status = ?"), append(args, *v) + } + if v := find.Name; v != nil { + where, args = append(where, "name = ?"), append(args, *v) + } + if v := find.VisibilityList; len(v) != 0 { + list := []string{} + for _, visibility := range v { + list = append(list, fmt.Sprintf("$%d", len(args)+1)) + args = append(args, visibility) + } + where = append(where, fmt.Sprintf("visibility in (%s)", strings.Join(list, ","))) + } + if v := find.Tag; v != nil { + where, args = append(where, "tag LIKE ?"), append(args, "%"+*v+"%") + } + + rows, err := s.db.QueryContext(ctx, ` + SELECT + id, + creator_id, + created_ts, + updated_ts, + row_status, + name, + link, + title, + description, + visibility, + tag, + og_metadata + FROM shortcut + WHERE `+strings.Join(where, " AND ")+` + ORDER BY created_ts DESC`, + args..., + ) + if err != nil { + return nil, err + } + defer rows.Close() + + list := make([]*storepb.Shortcut, 0) + for rows.Next() { + shortcut := &storepb.Shortcut{} + var rowStatus, visibility, tags, openGraphMetadataString string + if err := rows.Scan( + &shortcut.Id, + &shortcut.CreatorId, + &shortcut.CreatedTs, + &shortcut.UpdatedTs, + &rowStatus, + &shortcut.Name, + &shortcut.Link, + &shortcut.Title, + &shortcut.Description, + &visibility, + &tags, + &openGraphMetadataString, + ); err != nil { + return nil, err + } + shortcut.RowStatus = convertStorepbRowStatus(rowStatus) + shortcut.Visibility = storepb.Visibility(storepb.Visibility_value[visibility]) + shortcut.Tags = strings.Split(tags, " ") + var ogMetadata storepb.OpenGraphMetadata + if err := protojson.Unmarshal([]byte(openGraphMetadataString), &ogMetadata); err != nil { + return nil, err + } + shortcut.OgMetadata = &ogMetadata + list = append(list, shortcut) + } + + if err := rows.Err(); err != nil { + return nil, err + } + return list, nil +} + func (s *Store) GetShortcut(ctx context.Context, find *FindShortcut) (*Shortcut, error) { if find.ID != nil { if cache, ok := s.shortcutCache.Load(*find.ID); ok { @@ -298,6 +423,20 @@ func (s *Store) GetShortcut(ctx context.Context, find *FindShortcut) (*Shortcut, return shortcut, nil } +func (s *Store) GetShortcutV1(ctx context.Context, find *FindShortcut) (*storepb.Shortcut, error) { + shortcuts, err := s.ListShortcutsV1(ctx, find) + if err != nil { + return nil, err + } + + if len(shortcuts) == 0 { + return nil, nil + } + + shortcut := shortcuts[0] + return shortcut, nil +} + func (s *Store) DeleteShortcut(ctx context.Context, delete *DeleteShortcut) error { if _, err := s.db.ExecContext(ctx, `DELETE FROM shortcut WHERE id = ?`, delete.ID); err != nil { return err diff --git a/test/store/shortcut_test.go b/test/store/shortcut_test.go index ced444a..b093cba 100644 --- a/test/store/shortcut_test.go +++ b/test/store/shortcut_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + storepb "github.com/boojack/slash/proto/gen/store" "github.com/boojack/slash/store" "github.com/stretchr/testify/require" ) @@ -13,17 +14,17 @@ func TestShortcutStore(t *testing.T) { ts := NewTestingStore(ctx, t) user, err := createTestingAdminUser(ctx, ts) require.NoError(t, err) - shortcut, err := ts.CreateShortcut(ctx, &store.Shortcut{ - CreatorID: user.ID, - Name: "test", - Link: "https://test.link", - Description: "A test shortcut", - Visibility: store.VisibilityPrivate, - Tag: "test link", - OpenGraphMetadata: &store.OpenGraphMetadata{}, + shortcut, err := ts.CreateShortcutV1(ctx, &storepb.Shortcut{ + CreatorId: user.ID, + Name: "test", + Link: "https://test.link", + Description: "A test shortcut", + Visibility: storepb.Visibility_PRIVATE, + Tags: []string{"test", "shortcut"}, + OgMetadata: &storepb.OpenGraphMetadata{}, }) require.NoError(t, err) - shortcuts, err := ts.ListShortcuts(ctx, &store.FindShortcut{ + shortcuts, err := ts.ListShortcutsV1(ctx, &store.FindShortcut{ CreatorID: &user.ID, }) require.NoError(t, err) @@ -31,22 +32,21 @@ func TestShortcutStore(t *testing.T) { require.Equal(t, shortcut, shortcuts[0]) newLink := "https://new.link" updatedShortcut, err := ts.UpdateShortcut(ctx, &store.UpdateShortcut{ - ID: shortcut.ID, + ID: shortcut.Id, Link: &newLink, }) require.NoError(t, err) require.Equal(t, newLink, updatedShortcut.Link) tag := "test" - shortcut, err = ts.GetShortcut(ctx, &store.FindShortcut{ + shortcut, err = ts.GetShortcutV1(ctx, &store.FindShortcut{ Tag: &tag, }) require.NoError(t, err) - require.Equal(t, updatedShortcut, shortcut) err = ts.DeleteShortcut(ctx, &store.DeleteShortcut{ - ID: shortcut.ID, + ID: shortcut.Id, }) require.NoError(t, err) - shortcuts, err = ts.ListShortcuts(ctx, &store.FindShortcut{ + shortcuts, err = ts.ListShortcutsV1(ctx, &store.FindShortcut{ CreatorID: &user.ID, }) require.NoError(t, err) diff --git a/test/store/user_test.go b/test/store/user_test.go index a200881..be028ad 100644 --- a/test/store/user_test.go +++ b/test/store/user_test.go @@ -41,7 +41,7 @@ func TestUserStore(t *testing.T) { users, err = ts.ListUsers(ctx, &store.FindUser{}) require.NoError(t, err) require.Equal(t, 0, len(users)) - shortcuts, err := ts.ListShortcuts(ctx, &store.FindShortcut{}) + shortcuts, err := ts.ListShortcutsV1(ctx, &store.FindShortcut{}) require.NoError(t, err) require.Equal(t, 0, len(shortcuts)) }