From 9e1a192f93d6afd2d6fb3ace0a420278684f20b7 Mon Sep 17 00:00:00 2001 From: Tigor Hutasuhut Date: Sun, 25 Aug 2024 22:10:57 +0700 Subject: [PATCH] images: implemented ListImages handler --- go/api/images_list.go | 42 +++++++-------- go/server/image_handlers.go | 82 +++++++++++++++++++++++++++++- schemas/proto/images/v1/list.proto | 6 +++ 3 files changed, 107 insertions(+), 23 deletions(-) diff --git a/go/api/images_list.go b/go/api/images_list.go index 066deac..f278680 100644 --- a/go/api/images_list.go +++ b/go/api/images_list.go @@ -50,35 +50,30 @@ type ImageListRequest struct { // - Author's url var imagesFTSBM25 = fmt.Sprintf("bm25(%s, 25, 20, 10, 5, 3, 3)", ImagesFts5.TableName()) -func (request ImageListRequest) Statement() Statement { - queriedImages := CTE("queried_images") - - return WITH( - queriedImages.AS(request.SelectStatement()), - )( - SELECT(queriedImages.AllColumns()). - FROM(queriedImages). - ORDER_BY( - Images.Device.ASC(), - Images.Subreddit.ASC(), - ), - ) -} - -func (request ImageListRequest) SelectStatement() SelectStatement { +func (request ImageListRequest) Statement() SelectStatement { cond := request.WhereExpression() - from := Images if len(request.Search) > 0 { cond.AND(ImagesFts5.ImagesFts5.EQ(String(request.Search))) - from.INNER_JOIN(ImagesFts5, Images.PostName.EQ(ImagesFts5.PostName)) - stmt := SELECT(Images.AllColumns).FROM(from).WHERE(cond) + stmt := SELECT( + Images.AllColumns, Devices.Slug, Devices.Name, + ). + FROM( + Images. + INNER_JOIN(Devices, Devices.Slug.EQ(Images.Device)). + INNER_JOIN(ImagesFts5, Images.PostName.EQ(ImagesFts5.PostName)), + ). + WHERE(cond) if request.Limit > 0 { stmt.LIMIT(request.Limit) } return stmt.ORDER_BY(RawString(imagesFTSBM25).DESC()) } - stmt := SELECT(Images.AllColumns).FROM(from).WHERE(cond) + stmt := SELECT( + Images.AllColumns, Devices.Slug, Devices.Name, + ). + FROM(Images.INNER_JOIN(Devices, Devices.Slug.EQ(Images.Device))). + WHERE(cond) if request.Limit > 0 { stmt.LIMIT(request.Limit) } @@ -132,10 +127,15 @@ func (request ImageListRequest) WhereExpression() BoolExpression { return cond } +type ImageListResponse struct { + model.Images + DeviceName string +} + // ImageList list images by request. // // Results are always grouped by device then grouped by subreddit after sorting. -func (api *API) ImageList(ctx context.Context, request ImageListRequest) (images []model.Images, err error) { +func (api *API) ImageList(ctx context.Context, request ImageListRequest) (images []ImageListResponse, err error) { ctx, span := tracer.Start(ctx, "ImageList") defer func() { telemetry.EndWithStatus(span, err) }() diff --git a/go/server/image_handlers.go b/go/server/image_handlers.go index 6ef42f3..b74eca5 100644 --- a/go/server/image_handlers.go +++ b/go/server/image_handlers.go @@ -1,7 +1,9 @@ package server import ( + "cmp" "context" + "slices" "connectrpc.com/connect" "github.com/tigorlazuardi/bluemage/go/api" @@ -17,6 +19,13 @@ type ImageHandler struct { var imageConverter = converter.ImageConverterImpl{} func (im *ImageHandler) ListImages(ctx context.Context, request *connect.Request[images.ListImagesRequest]) (*connect.Response[images.ListImagesResponse], error) { + if request.Msg.Limit < 1 { + request.Msg.Limit = 50 + } + if request.Msg.Limit > 1000 { + request.Msg.Limit = 1000 + } + listRequest := imageConverter.ProtoListImagesRequestToAPIImagesRequest(request.Msg) list, err := im.API.ImageList(ctx, listRequest) @@ -24,9 +33,19 @@ func (im *ImageHandler) ListImages(ctx context.Context, request *connect.Request return nil, errs.IntoConnectError(err) } - _ = list + group := jetImagesToProtoGroupedByDeviceImages(list) + resp := &images.ListImagesResponse{ + Devices: group, + } + if request.Msg.After > 0 && len(group) >= int(request.Msg.Limit) { + resp.After = &list[len(list)-1].CreatedAt + } - panic("not implemented") // TODO: Implement + if request.Msg.Before > 0 && len(group) >= int(request.Msg.Limit) { + resp.Before = &list[0].CreatedAt + } + + return connect.NewResponse(resp), nil } func (im *ImageHandler) DeleteImages(ctx context.Context, request *connect.Request[images.DeleteImagesRequest]) (*connect.Response[images.DeleteImagesResponse], error) { @@ -36,3 +55,62 @@ func (im *ImageHandler) DeleteImages(ctx context.Context, request *connect.Reque func (im *ImageHandler) GetImage(ctx context.Context, request *connect.Request[images.GetImageRequest]) (*connect.Response[images.GetImageResponse], error) { panic("not implemented") // TODO: Implement } + +func jetImagesToProtoGroupedByDeviceImages(jetImages []api.ImageListResponse) (out []*images.GroupedByDeviceImages) { + if jetImages == nil { + return nil + } + if len(jetImages) == 0 { + return []*images.GroupedByDeviceImages{} + } + + type device struct { + slug string + name string + } + + grouped := map[device]map[string][]*images.Image{} + + for _, jetImage := range jetImages { + device := device{ + slug: jetImage.Device, + name: jetImage.DeviceName, + } + subreddit := jetImage.Subreddit + if grouped[device] == nil { + grouped[device] = map[string][]*images.Image{} + } + + if grouped[device][subreddit] == nil { + grouped[device][subreddit] = []*images.Image{} + } + + grouped[device][subreddit] = append(grouped[device][subreddit], imageConverter.JetImageToProtoImage(jetImage.Images)) + } + + out = make([]*images.GroupedByDeviceImages, 0, len(grouped)) + + for device, subreddits := range grouped { + group := &images.GroupedByDeviceImages{ + Slug: device.slug, + Name: device.name, + Subreddits: make([]*images.GroupedBySubredditImages, 0, len(subreddits)), + } + for subreddit, img := range subreddits { + group.Subreddits = append(group.Subreddits, &images.GroupedBySubredditImages{ + Subreddit: subreddit, + Images: img, + }) + } + slices.SortFunc(group.Subreddits, func(a, b *images.GroupedBySubredditImages) int { + return cmp.Compare(a.Subreddit, b.Subreddit) + }) + out = append(out, group) + } + + slices.SortFunc(out, func(a, b *images.GroupedByDeviceImages) int { + return cmp.Compare(a.Name, b.Name) + }) + + return out +} diff --git a/schemas/proto/images/v1/list.proto b/schemas/proto/images/v1/list.proto index 0f305b3..4c766d7 100644 --- a/schemas/proto/images/v1/list.proto +++ b/schemas/proto/images/v1/list.proto @@ -71,6 +71,12 @@ message ListImagesRequest { message ListImagesResponse { repeated GroupedByDeviceImages devices = 1; + + // after is set if there are more images + // after the last given image. optional int64 after = 2; + + // before is set if there are more images + // before the first image in the result. optional int64 before = 3; }