subreddits: implemented create subreddit rpc endpoint

This commit is contained in:
Tigor Hutasuhut 2024-08-11 10:23:20 +07:00
parent 114a6d5116
commit 8ff3bc4fd8
6 changed files with 145 additions and 7 deletions

View file

@ -0,0 +1,38 @@
package api
import (
"context"
"errors"
"connectrpc.com/connect"
"github.com/aarondl/opt/omit"
"github.com/mattn/go-sqlite3"
"github.com/tigorlazuardi/bluemage/go/gen/models"
"github.com/tigorlazuardi/bluemage/go/pkg/errs"
)
func (api *API) SubredditCreate(ctx context.Context, subreddit *models.Subreddit) (err error) {
// TODO: add check to Reddit API to see if subreddit exists.
api.lockf(func() {
_, err = models.Subreddits.Insert(ctx, api.DB, &models.SubredditSetter{
Name: omit.From(subreddit.Name),
DisableScheduler: omit.From(subreddit.DisableScheduler),
Type: omit.From(subreddit.Type),
Schedule: omit.From(subreddit.Schedule),
Countback: omit.From(subreddit.Countback),
})
})
if err != nil {
if sqlite3err := new(sqlite3.Error); errors.As(err, &sqlite3err) {
if sqlite3err.Code == sqlite3.ErrConstraint {
return errs.
Wrapw(err, "subreddit already exists", "input", subreddit).
Code(connect.CodeAlreadyExists)
}
}
return errs.Wrap(err, "failed to insert subreddit")
}
return nil
}

View file

@ -1,4 +1,4 @@
package converter package converts
import ( import (
"github.com/aarondl/opt/omit" "github.com/aarondl/opt/omit"

21
go/converts/subreddit.go Normal file
View file

@ -0,0 +1,21 @@
package converts
import (
"github.com/tigorlazuardi/bluemage/go/gen/models"
subreddits "github.com/tigorlazuardi/bluemage/go/gen/proto/subreddits/v1"
)
// goverter:converter
// goverter:extend BoolToInt8 SubredditTypeToString Int8ToBool PtrBoolToOmitInt8
// goverter:extend PtrStringToOmitString PtrFloat64ToOmitFloat64 PtrInt32ToOmitInt32
type SubredditConverter interface {
// goverter:ignore CreatedAt UpdatedAt R CoverImageID
CreateSubredditRequestToModelsSubreddit(*subreddits.CreateSubredditRequest) *models.Subreddit
}
func SubredditTypeToString(subType subreddits.SubredditType) string {
if subType == subreddits.SubredditType_SUBREDDIT_TYPE_USER {
return "user"
}
return "r"
}

View file

@ -19,16 +19,16 @@ type DeviceHandler struct {
v1connect.UnimplementedDeviceServiceHandler v1connect.UnimplementedDeviceServiceHandler
} }
var convert converter.DeviceConverterImpl var deviceConvert converter.DeviceConverterImpl
// CreateDevice implements v1connect.DeviceServiceHandler. // CreateDevice implements v1connect.DeviceServiceHandler.
func (d *DeviceHandler) CreateDevice(ctx context.Context, request *connect.Request[device.CreateDeviceRequest]) (*connect.Response[device.CreateDeviceResponse], error) { func (d *DeviceHandler) CreateDevice(ctx context.Context, request *connect.Request[device.CreateDeviceRequest]) (*connect.Response[device.CreateDeviceResponse], error) {
dev := convert.CreateDeviceRequestToModelsDevice(request.Msg) dev := deviceConvert.CreateDeviceRequestToModelsDevice(request.Msg)
dev, err := d.API.DevicesCreate(ctx, dev) dev, err := d.API.DevicesCreate(ctx, dev)
if err != nil { if err != nil {
return nil, errs.IntoConnectError(err) return nil, errs.IntoConnectError(err)
} }
devResp := convert.ModelsDeviceToCreateDeviceResponse(dev) devResp := deviceConvert.ModelsDeviceToCreateDeviceResponse(dev)
return connect.NewResponse(devResp), nil return connect.NewResponse(devResp), nil
} }
@ -39,7 +39,7 @@ func (d *DeviceHandler) GetDevice(ctx context.Context, request *connect.Request[
return nil, errs.IntoConnectError(err) return nil, errs.IntoConnectError(err)
} }
devResp := convert.ModelsDeviceToGetDeviceResponse(dev) devResp := deviceConvert.ModelsDeviceToGetDeviceResponse(dev)
return connect.NewResponse(devResp), nil return connect.NewResponse(devResp), nil
} }
@ -56,7 +56,7 @@ func (d *DeviceHandler) ListDevices(ctx context.Context, request *connect.Reques
// Only fields that are set in the request will be updated. // Only fields that are set in the request will be updated.
func (de *DeviceHandler) UpdateDevice(ctx context.Context, request *connect.Request[device.UpdateDeviceRequest]) (*connect.Response[device.UpdateDeviceResponse], error) { func (de *DeviceHandler) UpdateDevice(ctx context.Context, request *connect.Request[device.UpdateDeviceRequest]) (*connect.Response[device.UpdateDeviceResponse], error) {
slug := request.Msg.Slug slug := request.Msg.Slug
set := convert.DeviceSetterProtoToModelsDeviceSetter(request.Msg.Set) set := deviceConvert.DeviceSetterProtoToModelsDeviceSetter(request.Msg.Set)
err := de.API.DevicesUpdate(ctx, slug, set) err := de.API.DevicesUpdate(ctx, slug, set)
if err != nil { if err != nil {
return nil, errs.IntoConnectError(err) return nil, errs.IntoConnectError(err)

View file

@ -0,0 +1,79 @@
package server
import (
"context"
"connectrpc.com/connect"
"github.com/tigorlazuardi/bluemage/go/api"
"github.com/tigorlazuardi/bluemage/go/gen/converter"
subreddits "github.com/tigorlazuardi/bluemage/go/gen/proto/subreddits/v1"
"github.com/tigorlazuardi/bluemage/go/gen/proto/subreddits/v1/v1connect"
"github.com/tigorlazuardi/bluemage/go/pkg/errs"
)
var subredditConverter = converter.SubredditConverterImpl{}
type SubredditHandler struct {
API *api.API
v1connect.UnimplementedSubredditsServiceHandler
}
// CreateSubreddit creates a new subreddit
// Returns the subreddit name.
//
// Returns the following error codes:
// - connect.CodeAlreadyExists if subreddit with the same name already exists.
// - connect.CodeInvalidArgument if validation failed, e.g. Invalid schedule cron format.
// - connect.CodeNotFound if the subreddit does not exist.
func (su *SubredditHandler) CreateSubreddit(ctx context.Context, request *connect.Request[subreddits.CreateSubredditRequest]) (*connect.Response[subreddits.CreateSubredditResponse], error) {
data := subredditConverter.CreateSubredditRequestToModelsSubreddit(request.Msg)
if err := su.API.SubredditCreate(ctx, data); err != nil {
return nil, errs.IntoConnectError(err)
}
resp := &subreddits.CreateSubredditResponse{
Name: data.Name,
}
return connect.NewResponse(resp), nil
}
// GetSubreddit returns a subreddit by its name.
//
// Returns error with connect.CodeNotFound if subreddit does not exist.
func (su *SubredditHandler) GetSubreddit(_ context.Context, _ *connect.Request[subreddits.GetSubredditRequest]) (*connect.Response[subreddits.GetSubredditResponse], error) {
panic("not implemented") // TODO: Implement
}
// ListSubreddits returns a list of subreddits.
func (su *SubredditHandler) ListSubreddits(_ context.Context, _ *connect.Request[subreddits.ListSubredditsRequest]) (*connect.Response[subreddits.ListSubredditsResponse], error) {
panic("not implemented") // TODO: Implement
}
// UpdateSubreddit updates a subreddit.
//
// Only the fields that are set in the request will be updated.
//
// Returns error with connect.CodeNotFound if subreddit does not exist.
func (su *SubredditHandler) UpdateSubreddit(_ context.Context, _ *connect.Request[subreddits.UpdateSubredditRequest]) (*connect.Response[subreddits.UpdateSubredditResponse], error) {
panic("not implemented") // TODO: Implement
}
// DeleteSubreddit deletes a subreddit.
//
// Returns error with connect.CodeNotFound if subreddit does not exist.
func (su *SubredditHandler) DeleteSubreddit(_ context.Context, _ *connect.Request[subreddits.DeleteSubredditRequest]) (*connect.Response[subreddits.DeleteSubredditResponse], error) {
panic("not implemented") // TODO: Implement
}
// ResolveSubredditName resolves the given subreddit name.
//
// The returned resolved_name is the name that is actually
// used in Reddit.
//
// Returns error with connect.CodeNotFound if subreddit does not exist.
// So this rpc endpoint also acts to check subreddit's existence.
func (su *SubredditHandler) ResolveSubredditName(_ context.Context, _ *connect.Request[subreddits.ResolveSubredditNameRequest]) (*connect.Response[subreddits.ResolveSubredditNameResponse], error) {
panic("not implemented") // TODO: Implement
}

View file

@ -18,7 +18,7 @@ message CreateSubredditRequest {
// schedule is cron job spec to set schedule on when // schedule is cron job spec to set schedule on when
// the runner for this subreddit runs. // the runner for this subreddit runs.
string schedule = 4 [(buf.validate.field).string.min_len = 1]; string schedule = 4 [(buf.validate.field).string.min_len = 1];
uint32 countback = 5 [(buf.validate.field).uint32.gt = 0]; int64 countback = 5 [(buf.validate.field).int64.gt = 0];
} }
enum SubredditType { enum SubredditType {