refactor: api now uses it's own request type and handlers

will now handles the conversion of the request to the api request type
This commit is contained in:
Tigor Hutasuhut 2024-08-15 10:23:56 +07:00
parent 8bb8cb30ec
commit cf14079f1f
9 changed files with 187 additions and 133 deletions

View file

@ -5,11 +5,8 @@ import (
"sync" "sync"
"github.com/stephenafamo/bob" "github.com/stephenafamo/bob"
"github.com/tigorlazuardi/bluemage/go/gen/converter"
) )
var convert converter.DeviceConverterImpl
type API struct { type API struct {
mu sync.Mutex mu sync.Mutex
Executor bob.Executor Executor bob.Executor
@ -21,3 +18,10 @@ func (api *API) lockf(f func()) {
defer api.mu.Unlock() defer api.mu.Unlock()
f() f()
} }
type Sort string
const (
SortAsc Sort = "asc"
SortDesc Sort = "desc"
)

View file

@ -1,9 +1,6 @@
package api package api
import ( import (
"strings"
device "github.com/tigorlazuardi/bluemage/go/gen/proto/device/v1"
"github.com/tigorlazuardi/bluemage/go/pkg/errs" "github.com/tigorlazuardi/bluemage/go/pkg/errs"
"golang.org/x/net/context" "golang.org/x/net/context"
@ -12,19 +9,30 @@ import (
. "github.com/tigorlazuardi/bluemage/go/gen/jet/table" . "github.com/tigorlazuardi/bluemage/go/gen/jet/table"
) )
func listDevicesRequestSelectStatement(req *device.ListDevicesRequest) SelectStatement { type ListDevicesRequest struct {
Search string
Disabled *bool
Limit int64
Offset int64
OrderBy string
Sort Sort
}
func (request ListDevicesRequest) Statement() SelectStatement {
cond := Bool(true) cond := Bool(true)
switch req.Disabled { if d := request.Disabled; d != nil {
case device.DisabledFilter_DISABLED_FILTER_TRUE: disabled := *d
cond.AND(Devices.Disabled.EQ(Int(1))) if disabled {
case device.DisabledFilter_DISABLED_FILTER_FALSE: cond.AND(Devices.Disabled.EQ(Int(1)))
cond.AND(Devices.Disabled.EQ(Int(0))) } else {
cond.AND(Devices.Disabled.EQ(Int(0)))
}
} }
if req.Search != "" { if request.Search != "" {
cond.AND( cond.AND(
Devices.Name.LIKE(String("%" + req.Search + "%")). Devices.Name.LIKE(String("%" + request.Search + "%")).
OR(Devices.Slug.LIKE(String("%" + req.Search + "%"))), OR(Devices.Slug.LIKE(String("%" + request.Search + "%"))),
) )
} }
@ -32,45 +40,35 @@ func listDevicesRequestSelectStatement(req *device.ListDevicesRequest) SelectSta
FROM(Devices). FROM(Devices).
WHERE(cond) WHERE(cond)
if req.Limit > 0 { if request.Limit > 0 {
stmt.LIMIT(int64(req.Limit)) stmt.LIMIT(request.Limit)
} }
if req.Offset > 0 { if request.Offset > 0 {
stmt.OFFSET(int64(req.Offset)) stmt.OFFSET(request.Offset)
} }
if request.OrderBy == "" {
if req.OrderBy == device.OrderBy_ORDER_BY_UNSPECIFIED {
return stmt.ORDER_BY(Devices.CreatedAt.DESC()) return stmt.ORDER_BY(Devices.CreatedAt.DESC())
} }
orderByField, _ := strings.CutPrefix(device.OrderBy_name[int32(req.OrderBy)], "ORDER_BY_") orderBy := StringColumn(request.OrderBy)
orderByField = strings.ToLower(orderByField) if request.Sort == SortDesc {
orderBy := StringColumn(orderByField)
if req.Sort == device.Sort_SORT_DESCENDING {
return stmt.ORDER_BY(orderBy.DESC()) return stmt.ORDER_BY(orderBy.DESC())
} else { } else {
return stmt.ORDER_BY(orderBy.ASC()) return stmt.ORDER_BY(orderBy.ASC())
} }
} }
func (api *API) DevicesList(ctx context.Context, req *device.ListDevicesRequest) (resp *device.ListDevicesResponse, err error) { func (api *API) DevicesList(ctx context.Context, req ListDevicesRequest) (resp []model.Devices, err error) {
ctx, span := tracer.Start(ctx, "DevicesList") ctx, span := tracer.Start(ctx, "DevicesList")
defer span.End() defer span.End()
resp = &device.ListDevicesResponse{} stmt := req.Statement()
stmt := listDevicesRequestSelectStatement(req) if err := stmt.QueryContext(ctx, api.DB, &resp); err != nil {
var out []model.Devices
if err := stmt.QueryContext(ctx, api.DB, &out); err != nil {
return resp, errs.Wrapw(err, "failed to list devices", return resp, errs.Wrapw(err, "failed to list devices",
"request", req, "request", req,
"query", stmt.DebugSql(), "query", stmt.DebugSql(),
) )
} }
for _, result := range out {
resp.Devices = append(resp.Devices, convert.JetModelDeviceToGetDeviceResponse(result))
}
return resp, err return resp, err
} }

View file

@ -1,7 +1,9 @@
package converts package converts
import ( import (
"github.com/aarondl/opt/omit" "strings"
"github.com/tigorlazuardi/bluemage/go/api"
"github.com/tigorlazuardi/bluemage/go/gen/jet/model" "github.com/tigorlazuardi/bluemage/go/gen/jet/model"
"github.com/tigorlazuardi/bluemage/go/gen/models" "github.com/tigorlazuardi/bluemage/go/gen/models"
device "github.com/tigorlazuardi/bluemage/go/gen/proto/device/v1" device "github.com/tigorlazuardi/bluemage/go/gen/proto/device/v1"
@ -24,6 +26,9 @@ import (
// goverter:extend Int64ToOmitInt64 // goverter:extend Int64ToOmitInt64
// goverter:extend Float64ToOmitFloat64 // goverter:extend Float64ToOmitFloat64
// goverter:extend StringToOmitString // goverter:extend StringToOmitString
// goverter:extend DeviceDisabledFilterToPtrBool
// goverter:extend DeviceOrderByToString
// goverter:extend DeviceSortToAPISort
type DeviceConverter interface { type DeviceConverter interface {
// goverter:ignore CreatedAt UpdatedAt // goverter:ignore CreatedAt UpdatedAt
// goverter:map Nsfw NSFW // goverter:map Nsfw NSFW
@ -32,86 +37,41 @@ type DeviceConverter interface {
ModelsDeviceToCreateDeviceResponse(*models.Device) *device.CreateDeviceResponse ModelsDeviceToCreateDeviceResponse(*models.Device) *device.CreateDeviceResponse
// goverter:ignore state sizeCache unknownFields // goverter:ignore state sizeCache unknownFields
// goverter:useZeroValueOnPointerInconsistency // goverter:useZeroValueOnPointerInconsistency
JetModelDeviceToGetDeviceResponse(model.Devices) *device.GetDeviceResponse JetModelDeviceToProtoDevice(model.Devices) *device.Device
// goverter:useZeroValueOnPointerInconsistency
ListDevicesRequestToAPIListDevicesRequest(*device.ListDevicesRequest) api.ListDevicesRequest
// goverter:ignore Slug SingleFolderMode CreatedAt UpdatedAt // goverter:ignore Slug SingleFolderMode CreatedAt UpdatedAt
// goverter:map Nsfw NSFW // goverter:map Nsfw NSFW
DeviceSetterProtoToModelsDeviceSetter(*device.DeviceSetter) *models.DeviceSetter DeviceSetterProtoToModelsDeviceSetter(*device.DeviceSetter) *models.DeviceSetter
} }
func BoolToInt8(b bool) int8 { func DeviceDisabledFilterToPtrBool(f device.DisabledFilter) *bool {
if b { switch f {
return 1 case device.DisabledFilter_DISABLED_FILTER_TRUE:
b := true
return &b
case device.DisabledFilter_DISABLED_FILTER_FALSE:
b := false
return &b
default:
return nil
} }
return 0
} }
func PtrBoolToOmitInt8(b *bool) omit.Val[int8] { func DeviceOrderByToString(order device.OrderBy) string {
if b == nil { if order == device.OrderBy_ORDER_BY_UNSPECIFIED {
return omit.Val[int8]{} return ""
} }
v := *b field, _ := strings.CutPrefix(device.OrderBy_name[int32(order)], "ORDER_BY_")
if v { field = strings.ToLower(field)
return omit.From(int8(1)) return field
}
func DeviceSortToAPISort(sort device.Sort) api.Sort {
if sort == device.Sort_SORT_DESCENDING {
return api.SortDesc
} }
return omit.From(int8(0)) return api.SortDesc
}
func PtrStringToOmitString(s *string) omit.Val[string] {
return omit.FromPtr(s)
}
func StringToOmitString(s string) omit.Val[string] {
return omit.From(s)
}
func PtrFloat64ToOmitFloat64(f *float64) omit.Val[float64] {
return omit.FromPtr(f)
}
func Float64ToOmitFloat64(f float64) omit.Val[float64] {
return omit.From(f)
}
func PtrIntToOmitInt(i *int) omit.Val[int] {
return omit.FromPtr(i)
}
func IntToOmitInt(i int) omit.Val[int] {
return omit.From(i)
}
func PtrInt8ToOmitInt8(i *int8) omit.Val[int8] {
return omit.FromPtr(i)
}
func Int8ToOmitInt8(i int8) omit.Val[int8] {
return omit.From(i)
}
func PtrInt32ToOmitInt32(i *int32) omit.Val[int32] {
return omit.FromPtr(i)
}
func Int32ToOmitInt32(i int32) omit.Val[int32] {
return omit.From(i)
}
func PtrInt64ToOmitInt64(i *int64) omit.Val[int64] {
return omit.FromPtr(i)
}
func Int64ToOmitInt64(i int64) omit.Val[int64] {
return omit.From(i)
}
func BoolToOmitInt8(b bool) omit.Val[int8] {
if b {
return omit.From(int8(1))
}
return omit.From(int8(0))
}
func Int8ToBool(i int8) bool {
return i > 0
} }

80
go/converts/utils.go Normal file
View file

@ -0,0 +1,80 @@
package converts
import "github.com/aarondl/opt/omit"
func BoolToInt8(b bool) int8 {
if b {
return 1
}
return 0
}
func PtrBoolToOmitInt8(b *bool) omit.Val[int8] {
if b == nil {
return omit.Val[int8]{}
}
v := *b
if v {
return omit.From(int8(1))
}
return omit.From(int8(0))
}
func PtrStringToOmitString(s *string) omit.Val[string] {
return omit.FromPtr(s)
}
func StringToOmitString(s string) omit.Val[string] {
return omit.From(s)
}
func PtrFloat64ToOmitFloat64(f *float64) omit.Val[float64] {
return omit.FromPtr(f)
}
func Float64ToOmitFloat64(f float64) omit.Val[float64] {
return omit.From(f)
}
func PtrIntToOmitInt(i *int) omit.Val[int] {
return omit.FromPtr(i)
}
func IntToOmitInt(i int) omit.Val[int] {
return omit.From(i)
}
func PtrInt8ToOmitInt8(i *int8) omit.Val[int8] {
return omit.FromPtr(i)
}
func Int8ToOmitInt8(i int8) omit.Val[int8] {
return omit.From(i)
}
func PtrInt32ToOmitInt32(i *int32) omit.Val[int32] {
return omit.FromPtr(i)
}
func Int32ToOmitInt32(i int32) omit.Val[int32] {
return omit.From(i)
}
func PtrInt64ToOmitInt64(i *int64) omit.Val[int64] {
return omit.FromPtr(i)
}
func Int64ToOmitInt64(i int64) omit.Val[int64] {
return omit.From(i)
}
func BoolToOmitInt8(b bool) omit.Val[int8] {
if b {
return omit.From(int8(1))
}
return omit.From(int8(0))
}
func Int8ToBool(i int8) bool {
return i > 0
}

View file

@ -39,16 +39,24 @@ func (d *DeviceHandler) GetDevice(ctx context.Context, request *connect.Request[
return nil, errs.IntoConnectError(err) return nil, errs.IntoConnectError(err)
} }
devResp := deviceConvert.JetModelDeviceToGetDeviceResponse(dev) data := deviceConvert.JetModelDeviceToProtoDevice(dev)
return connect.NewResponse(devResp), nil resp := &device.GetDeviceResponse{
Device: data,
}
return connect.NewResponse(resp), nil
} }
// ListDevices implements v1connect.DeviceServiceHandler. // ListDevices implements v1connect.DeviceServiceHandler.
func (d *DeviceHandler) ListDevices(ctx context.Context, request *connect.Request[device.ListDevicesRequest]) (*connect.Response[device.ListDevicesResponse], error) { func (d *DeviceHandler) ListDevices(ctx context.Context, request *connect.Request[device.ListDevicesRequest]) (*connect.Response[device.ListDevicesResponse], error) {
resp, err := d.API.DevicesList(ctx, request.Msg) listRequest := deviceConvert.ListDevicesRequestToAPIListDevicesRequest(request.Msg)
devices, err := d.API.DevicesList(ctx, listRequest)
if err != nil { if err != nil {
return nil, errs.IntoConnectError(err) return nil, errs.IntoConnectError(err)
} }
resp := &device.ListDevicesResponse{}
for _, device := range devices {
resp.Devices = append(resp.Devices, deviceConvert.JetModelDeviceToProtoDevice(device))
}
return connect.NewResponse(resp), nil return connect.NewResponse(resp), nil
} }

View file

@ -38,20 +38,3 @@ service DeviceService {
// CountDevices count the number of devices. // CountDevices count the number of devices.
rpc CountDevices(CountDevicesRequest) returns (CountDevicesResponse) {} rpc CountDevices(CountDevicesRequest) returns (CountDevicesResponse) {}
} }
message Device {
string slug = 1;
bool disabled = 2;
string name = 3;
double resolution_x = 4;
double resolution_y = 5;
double aspect_ratio_tolerance = 6;
int32 min_x = 7;
int32 min_y = 8;
int32 max_x = 9;
int32 max_y = 10;
bool nsfw = 11;
bool single_folder_mode = 12;
int64 created_at = 13;
int64 updated_at = 14;
}

View file

@ -3,7 +3,7 @@ syntax = "proto3";
package device.v1; package device.v1;
import "buf/validate/validate.proto"; import "buf/validate/validate.proto";
import "device/v1/device.proto"; import "device/v1/types.proto";
option go_package = "github.com/tigorlazuardi/bluemage/go/gen/proto/device/v1"; option go_package = "github.com/tigorlazuardi/bluemage/go/gen/proto/device/v1";

View file

@ -2,8 +2,7 @@ syntax = "proto3";
package device.v1; package device.v1;
// import "buf/validate/validate.proto"; import "device/v1/types.proto";
import "device/v1/get.proto";
option go_package = "github.com/tigorlazuardi/bluemage/go/gen/proto/device/v1"; option go_package = "github.com/tigorlazuardi/bluemage/go/gen/proto/device/v1";
@ -28,12 +27,12 @@ message ListDevicesRequest {
// if value is 0 or not given, limit is set to 25. // if value is 0 or not given, limit is set to 25.
// //
// if limit is higher than 100, it is clamped to 100. // if limit is higher than 100, it is clamped to 100.
uint32 limit = 3; int64 limit = 3;
// offset for the given data. // offset for the given data.
// //
// If offset is 0 or not given, the query begins from start. // If offset is 0 or not given, the query begins from start.
uint32 offset = 4; int64 offset = 4;
// order_by is the field to order the devices by. // order_by is the field to order the devices by.
// //
@ -47,7 +46,7 @@ message ListDevicesRequest {
} }
message ListDevicesResponse { message ListDevicesResponse {
repeated GetDeviceResponse devices = 1; repeated Device devices = 1;
} }
enum DisabledFilter { enum DisabledFilter {

View file

@ -0,0 +1,22 @@
syntax = "proto3";
package device.v1;
option go_package = "github.com/tigorlazuardi/bluemage/go/gen/proto/device/v1";
message Device {
string slug = 1;
bool disabled = 2;
string name = 3;
double resolution_x = 4;
double resolution_y = 5;
double aspect_ratio_tolerance = 6;
int32 min_x = 7;
int32 min_y = 8;
int32 max_x = 9;
int32 max_y = 10;
bool nsfw = 11;
bool single_folder_mode = 12;
int64 created_at = 13;
int64 updated_at = 14;
}