images: implemented ListImages handler
This commit is contained in:
parent
0fec03adbc
commit
9e1a192f93
|
@ -50,35 +50,30 @@ type ImageListRequest struct {
|
||||||
// - Author's url
|
// - Author's url
|
||||||
var imagesFTSBM25 = fmt.Sprintf("bm25(%s, 25, 20, 10, 5, 3, 3)", ImagesFts5.TableName())
|
var imagesFTSBM25 = fmt.Sprintf("bm25(%s, 25, 20, 10, 5, 3, 3)", ImagesFts5.TableName())
|
||||||
|
|
||||||
func (request ImageListRequest) Statement() Statement {
|
func (request ImageListRequest) Statement() SelectStatement {
|
||||||
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 {
|
|
||||||
cond := request.WhereExpression()
|
cond := request.WhereExpression()
|
||||||
from := Images
|
|
||||||
|
|
||||||
if len(request.Search) > 0 {
|
if len(request.Search) > 0 {
|
||||||
cond.AND(ImagesFts5.ImagesFts5.EQ(String(request.Search)))
|
cond.AND(ImagesFts5.ImagesFts5.EQ(String(request.Search)))
|
||||||
from.INNER_JOIN(ImagesFts5, Images.PostName.EQ(ImagesFts5.PostName))
|
stmt := SELECT(
|
||||||
stmt := SELECT(Images.AllColumns).FROM(from).WHERE(cond)
|
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 {
|
if request.Limit > 0 {
|
||||||
stmt.LIMIT(request.Limit)
|
stmt.LIMIT(request.Limit)
|
||||||
}
|
}
|
||||||
return stmt.ORDER_BY(RawString(imagesFTSBM25).DESC())
|
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 {
|
if request.Limit > 0 {
|
||||||
stmt.LIMIT(request.Limit)
|
stmt.LIMIT(request.Limit)
|
||||||
}
|
}
|
||||||
|
@ -132,10 +127,15 @@ func (request ImageListRequest) WhereExpression() BoolExpression {
|
||||||
return cond
|
return cond
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ImageListResponse struct {
|
||||||
|
model.Images
|
||||||
|
DeviceName string
|
||||||
|
}
|
||||||
|
|
||||||
// ImageList list images by request.
|
// ImageList list images by request.
|
||||||
//
|
//
|
||||||
// Results are always grouped by device then grouped by subreddit after sorting.
|
// 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")
|
ctx, span := tracer.Start(ctx, "ImageList")
|
||||||
defer func() { telemetry.EndWithStatus(span, err) }()
|
defer func() { telemetry.EndWithStatus(span, err) }()
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cmp"
|
||||||
"context"
|
"context"
|
||||||
|
"slices"
|
||||||
|
|
||||||
"connectrpc.com/connect"
|
"connectrpc.com/connect"
|
||||||
"github.com/tigorlazuardi/bluemage/go/api"
|
"github.com/tigorlazuardi/bluemage/go/api"
|
||||||
|
@ -17,6 +19,13 @@ type ImageHandler struct {
|
||||||
var imageConverter = converter.ImageConverterImpl{}
|
var imageConverter = converter.ImageConverterImpl{}
|
||||||
|
|
||||||
func (im *ImageHandler) ListImages(ctx context.Context, request *connect.Request[images.ListImagesRequest]) (*connect.Response[images.ListImagesResponse], error) {
|
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)
|
listRequest := imageConverter.ProtoListImagesRequestToAPIImagesRequest(request.Msg)
|
||||||
|
|
||||||
list, err := im.API.ImageList(ctx, listRequest)
|
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)
|
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) {
|
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) {
|
func (im *ImageHandler) GetImage(ctx context.Context, request *connect.Request[images.GetImageRequest]) (*connect.Response[images.GetImageResponse], error) {
|
||||||
panic("not implemented") // TODO: Implement
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -71,6 +71,12 @@ message ListImagesRequest {
|
||||||
|
|
||||||
message ListImagesResponse {
|
message ListImagesResponse {
|
||||||
repeated GroupedByDeviceImages devices = 1;
|
repeated GroupedByDeviceImages devices = 1;
|
||||||
|
|
||||||
|
// after is set if there are more images
|
||||||
|
// after the last given image.
|
||||||
optional int64 after = 2;
|
optional int64 after = 2;
|
||||||
|
|
||||||
|
// before is set if there are more images
|
||||||
|
// before the first image in the result.
|
||||||
optional int64 before = 3;
|
optional int64 before = 3;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue