From c83387652b2bc12064ca56de01db76b24a27fac1 Mon Sep 17 00:00:00 2001 From: Tigor Hutasuhut Date: Thu, 15 Aug 2024 15:07:17 +0700 Subject: [PATCH] api: implemented CountSubreddits API endpoint --- go/api/subreddits_count.go | 50 ++++++++++++++++++++ go/converts/subreddit.go | 15 ++++++ go/converts/utils.go | 4 ++ go/server/subreddit_handlers.go | 14 ++++++ schemas/proto/subreddits/v1/subreddits.proto | 6 +++ 5 files changed, 89 insertions(+) create mode 100644 go/api/subreddits_count.go diff --git a/go/api/subreddits_count.go b/go/api/subreddits_count.go new file mode 100644 index 0000000..d73cda8 --- /dev/null +++ b/go/api/subreddits_count.go @@ -0,0 +1,50 @@ +package api + +import ( + "context" + + . "github.com/go-jet/jet/v2/sqlite" + . "github.com/tigorlazuardi/bluemage/go/gen/jet/table" + "github.com/tigorlazuardi/bluemage/go/pkg/errs" + "github.com/tigorlazuardi/bluemage/go/pkg/telemetry" +) + +type CountSubredditsRequest struct { + Search string + Disabled *bool +} + +func (c CountSubredditsRequest) Statement() SelectStatement { + cond := Bool(true) + if c.Search != "" { + cond.AND(Subreddits.Name.LIKE(String("%" + c.Search + "%"))) + } + if c.Disabled != nil { + disabled := *c.Disabled + if disabled { + cond.AND(Subreddits.Disabled.EQ(Int(1))) + } else { + cond.AND(Subreddits.Disabled.EQ(Int(0))) + } + } + + return SELECT(COUNT(Subreddits.Name)).WHERE(cond) +} + +func (api *API) CountSubreddits(ctx context.Context, request CountSubredditsRequest) (count int64, err error) { + ctx, span := tracer.Start(ctx, "CountSubreddits") + defer func() { telemetry.EndWithStatus(span, err) }() + + stmt := request.Statement() + query, args := stmt.Sql() + err = api.DB.QueryRowContext(ctx, query, args...).Scan(&count) + if err != nil { + err = errs.Wrapw(err, "failed to count subreddits", + "request", request, + "query", stmt.DebugSql(), + ) + return count, err + } + + return count, err +} diff --git a/go/converts/subreddit.go b/go/converts/subreddit.go index ba09b11..871cf8c 100644 --- a/go/converts/subreddit.go +++ b/go/converts/subreddit.go @@ -2,6 +2,7 @@ package converts import ( "github.com/aarondl/opt/omit" + "github.com/tigorlazuardi/bluemage/go/api" "github.com/tigorlazuardi/bluemage/go/gen/models" subreddits "github.com/tigorlazuardi/bluemage/go/gen/proto/subreddits/v1" ) @@ -25,9 +26,13 @@ import ( // goverter:extend StringToOmitString // goverter:extend SubredditTypeToString // goverter:extend SubredditTypeToOmitString +// goverter:extend SubredditsDisabledFilterToPtrBool type SubredditConverter interface { // goverter:ignore CreatedAt UpdatedAt CoverImageID CreateSubredditRequestToModelsSubredditSetter(*subreddits.CreateSubredditRequest) *models.SubredditSetter + + // goverter:useZeroValueOnPointerInconsistency + ProtoCountSubredditsRequestToAPICountSubredditsRequest(*subreddits.CountSubredditsRequest) api.CountSubredditsRequest } func SubredditTypeToString(subType subreddits.SubredditType) string { @@ -43,3 +48,13 @@ func SubredditTypeToOmitString(subType subreddits.SubredditType) omit.Val[string } return omit.From("r") } + +func SubredditsDisabledFilterToPtrBool(filter subreddits.DisabledFilter) *bool { + switch filter { + case subreddits.DisabledFilter_DISABLED_FILTER_TRUE: + return boolPtr(true) + case subreddits.DisabledFilter_DISABLED_FILTER_FALSE: + return boolPtr(false) + } + return nil +} diff --git a/go/converts/utils.go b/go/converts/utils.go index 500c339..b5d1d2e 100644 --- a/go/converts/utils.go +++ b/go/converts/utils.go @@ -78,3 +78,7 @@ func BoolToOmitInt8(b bool) omit.Val[int8] { func Int8ToBool(i int8) bool { return i > 0 } + +func boolPtr(b bool) *bool { + return &b +} diff --git a/go/server/subreddit_handlers.go b/go/server/subreddit_handlers.go index 6ba1ebe..7fae7f1 100644 --- a/go/server/subreddit_handlers.go +++ b/go/server/subreddit_handlers.go @@ -19,6 +19,20 @@ type SubredditHandler struct { v1connect.UnimplementedSubredditsServiceHandler } +// Count the existing subreddits using given filter. +// Default values count all. +func (su *SubredditHandler) CountSubreddits(ctx context.Context, request *connect.Request[subreddits.CountSubredditsRequest]) (*connect.Response[subreddits.CountSubredditsResponse], error) { + countRequest := subredditConverter.ProtoCountSubredditsRequestToAPICountSubredditsRequest(request.Msg) + count, err := su.API.CountSubreddits(ctx, countRequest) + if err != nil { + return nil, errs.IntoConnectError(err) + } + resp := &subreddits.CountSubredditsResponse{ + Count: count, + } + return connect.NewResponse(resp), nil +} + // CreateSubreddit creates a new subreddit // Returns the subreddit name. // diff --git a/schemas/proto/subreddits/v1/subreddits.proto b/schemas/proto/subreddits/v1/subreddits.proto index f2950ee..0fbecaa 100644 --- a/schemas/proto/subreddits/v1/subreddits.proto +++ b/schemas/proto/subreddits/v1/subreddits.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package subreddits.v1; +import "subreddits/v1/count.proto"; import "subreddits/v1/create.proto"; import "subreddits/v1/delete.proto"; import "subreddits/v1/get.proto"; @@ -50,4 +51,9 @@ service SubredditsService { // Returns error with connect.CodeNotFound if subreddit does not exist. // So this rpc endpoint also acts to check subreddit's existence. rpc ResolveSubredditName(ResolveSubredditNameRequest) returns (ResolveSubredditNameResponse); + + // Count the existing subreddits using given filter. + // + // Default values count all. + rpc CountSubreddits(CountSubredditsRequest) returns (CountSubredditsResponse); }