From 107330e49b13bdc13e467f6b392c8be47e920a5e Mon Sep 17 00:00:00 2001 From: Tigor Hutasuhut Date: Wed, 7 Aug 2024 23:59:29 +0700 Subject: [PATCH] devices: added list devices endpoint --- Makefile | 2 +- go/api/devices_list.go | 91 +++++++++++++++++++++++++ go/{converter => converts}/converter.go | 0 go/server/device_handlers.go | 9 +++ schemas/proto/device/v1/list.proto | 10 ++- 5 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 go/api/devices_list.go rename go/{converter => converts}/converter.go (100%) diff --git a/Makefile b/Makefile index ba54943..c9f5e24 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ generate-go: migrate rm -rf go/gen/* (cd ./schemas/proto && buf generate --template buf.gen.go.yaml .) (cd go/gen && bobgen-sqlite --config ../bobgen.yaml) - (cd go && goverter gen -g 'output:file ../gen/converter/converter.go' -g 'output:package github.com/tigorlazuardi/bluemage/go/gen/converter' ./converter) + (cd go && goverter gen -g 'output:file ../gen/converter/converter.go' -g 'output:package github.com/tigorlazuardi/bluemage/go/gen/converter' ./converts) generate-web: rm -rf web/gen diff --git a/go/api/devices_list.go b/go/api/devices_list.go new file mode 100644 index 0000000..7026ab3 --- /dev/null +++ b/go/api/devices_list.go @@ -0,0 +1,91 @@ +package api + +import ( + "strings" + + "github.com/stephenafamo/bob" + "github.com/stephenafamo/bob/dialect/sqlite" + "github.com/stephenafamo/bob/dialect/sqlite/dialect" + "github.com/stephenafamo/bob/dialect/sqlite/sm" + "github.com/tigorlazuardi/bluemage/go/gen/converter" + "github.com/tigorlazuardi/bluemage/go/gen/models" + device "github.com/tigorlazuardi/bluemage/go/gen/proto/device/v1" + "github.com/tigorlazuardi/bluemage/go/pkg/errs" + "golang.org/x/net/context" +) + +var convert converter.DeviceConverterImpl + +func queryFromListDeviceRequest(req *device.ListDevicesRequest) (expr []bob.Mod[*dialect.SelectQuery]) { + expr = countQueryFromListDeviceRequest(req) + + if req.Limit > 0 { + expr = append(expr, sm.Limit(req.Limit)) + } + + if req.Offset > 0 { + expr = append(expr, sm.Offset(req.Offset)) + } + + if req.OrderBy == device.OrderBy_ORDER_BY_UNSPECIFIED { + return append(expr, sm.OrderBy(models.DeviceColumns.CreatedAt).Desc()) + } + + orderByField, _ := strings.CutPrefix(device.OrderBy_name[int32(req.OrderBy)], "ORDER_BY_") + orderByField = strings.ToLower(orderByField) + + orderBy := sm.OrderBy(sqlite.Quote(orderByField)) + if req.Sort == device.Sort_SORT_DESCENDING { + expr = append(expr, orderBy.Desc()) + } else { + expr = append(expr, orderBy.Asc()) + } + + return expr +} + +func countQueryFromListDeviceRequest(req *device.ListDevicesRequest) (expr []bob.Mod[*dialect.SelectQuery]) { + switch req.Disabled { + case device.DisabledFilter_DISABLED_FILTER_TRUE: + expr = append(expr, models.SelectWhere.Devices.Disabled.EQ(1)) + case device.DisabledFilter_DISABLED_FILTER_FALSE: + expr = append(expr, models.SelectWhere.Devices.Disabled.EQ(0)) + } + + if req.Search != "" { + arg := sqlite.Arg("%" + req.Search + "%") + expr = append(expr, + sm.Where( + models.DeviceColumns.Name.Like(arg).Or(models.DeviceColumns.Slug.Like(arg)), + ), + ) + } + return expr +} + +func (api *API) DevicesList(ctx context.Context, req *device.ListDevicesRequest) (resp *device.ListDevicesResponse, err error) { + resp = &device.ListDevicesResponse{} + results, err := models.Devices.Query(ctx, api.DB, queryFromListDeviceRequest(req)...).All() + if err != nil { + return resp, errs.Wrapw(err, "failed to list devices", "request", req) + } + if req.Disabled == device.DisabledFilter_DISABLED_FILTER_UNSPECIFIED && req.Search == "" { + const metricName = "devices.count" + metric, err := models.FindMetric(ctx, api.DB, metricName) + if err != nil { + return resp, errs.Wrapw(err, "failed to find devices count metric", "metric", metricName) + } + resp.Count = uint64(metric.Value) + } else { + count, err := models.Devices.Query(ctx, api.DB, countQueryFromListDeviceRequest(req)...).Count() + if err != nil { + return resp, errs.Wrapw(err, "failed to count query result") + } + resp.Count = uint64(count) + } + + for _, result := range results { + resp.Devices = append(resp.Devices, convert.ModelsDeviceToGetDeviceResponse(result)) + } + return resp, err +} diff --git a/go/converter/converter.go b/go/converts/converter.go similarity index 100% rename from go/converter/converter.go rename to go/converts/converter.go diff --git a/go/server/device_handlers.go b/go/server/device_handlers.go index a6f5d32..57f8d1e 100644 --- a/go/server/device_handlers.go +++ b/go/server/device_handlers.go @@ -55,3 +55,12 @@ func (d *DeviceHandler) GetDevice(ctx context.Context, request *connect.Request[ devResp := convert.ModelsDeviceToGetDeviceResponse(dev) return connect.NewResponse(devResp), nil } + +// ListDevices implements v1connect.DeviceServiceHandler. +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) + if err != nil { + return nil, errs.IntoConnectError(err) + } + return connect.NewResponse(resp), nil +} diff --git a/schemas/proto/device/v1/list.proto b/schemas/proto/device/v1/list.proto index f344d8b..ca661b3 100644 --- a/schemas/proto/device/v1/list.proto +++ b/schemas/proto/device/v1/list.proto @@ -20,8 +20,8 @@ message ListDevicesRequest { // disabled limit the listing devides only // to disabled devices. // - // default: false. - bool disabled = 2; + // If unspecified, devices with either status will be received. + DisabledFilter disabled = 2; // limits the number of results. // @@ -51,6 +51,12 @@ message ListDevicesResponse { repeated GetDeviceResponse devices = 2; } +enum DisabledFilter { + DISABLED_FILTER_UNSPECIFIED = 0; + DISABLED_FILTER_TRUE = 1; + DISABLED_FILTER_FALSE = 2; +} + enum OrderBy { ORDER_BY_UNSPECIFIED = 0; ORDER_BY_SLUG = 1;