Add filter and orderBy. Closes #55 (#73)

Reviewed-on: #73
Co-authored-by: Daniel Ponte <amigan@gmail.com>
Co-committed-by: Daniel Ponte <amigan@gmail.com>
This commit is contained in:
Daniel Ponte 2024-12-17 21:17:10 -05:00 committed by amigan
parent a29e63eb71
commit 9a3a32c1a2
8 changed files with 403 additions and 162 deletions

View file

@ -70,6 +70,19 @@ func (db *Postgres) InTx(ctx context.Context, f func(Store) error, opts pgx.TxOp
type dbLogger struct{}
func (m dbLogger) Log(ctx context.Context, level tracelog.LogLevel, msg string, data map[string]any) {
var rs string
// zerolog doesn't do this for us...
if i, ok := data["args"].([]interface{}); ok {
res := make([]string, 0, len(i))
for _, v := range i {
res = append(res, fmt.Sprintf("%T:%+v", v, v))
}
rs = strings.Join(res, ", ")
data["args"] = "ARRAY:[" + rs + "]"
}
log.Debug().Fields(data).Msg(msg)
}

View file

@ -1816,9 +1816,9 @@ func (_c *Store_GetTalkgroupsWithLearnedBySystem_Call) RunAndReturn(run func(con
return _c
}
// GetTalkgroupsWithLearnedBySystemCount provides a mock function with given fields: ctx, system
func (_m *Store) GetTalkgroupsWithLearnedBySystemCount(ctx context.Context, system int32) (int64, error) {
ret := _m.Called(ctx, system)
// GetTalkgroupsWithLearnedBySystemCount provides a mock function with given fields: ctx, system, filter
func (_m *Store) GetTalkgroupsWithLearnedBySystemCount(ctx context.Context, system int32, filter *string) (int64, error) {
ret := _m.Called(ctx, system, filter)
if len(ret) == 0 {
panic("no return value specified for GetTalkgroupsWithLearnedBySystemCount")
@ -1826,17 +1826,17 @@ func (_m *Store) GetTalkgroupsWithLearnedBySystemCount(ctx context.Context, syst
var r0 int64
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, int32) (int64, error)); ok {
return rf(ctx, system)
if rf, ok := ret.Get(0).(func(context.Context, int32, *string) (int64, error)); ok {
return rf(ctx, system, filter)
}
if rf, ok := ret.Get(0).(func(context.Context, int32) int64); ok {
r0 = rf(ctx, system)
if rf, ok := ret.Get(0).(func(context.Context, int32, *string) int64); ok {
r0 = rf(ctx, system, filter)
} else {
r0 = ret.Get(0).(int64)
}
if rf, ok := ret.Get(1).(func(context.Context, int32) error); ok {
r1 = rf(ctx, system)
if rf, ok := ret.Get(1).(func(context.Context, int32, *string) error); ok {
r1 = rf(ctx, system, filter)
} else {
r1 = ret.Error(1)
}
@ -1852,13 +1852,14 @@ type Store_GetTalkgroupsWithLearnedBySystemCount_Call struct {
// GetTalkgroupsWithLearnedBySystemCount is a helper method to define mock.On call
// - ctx context.Context
// - system int32
func (_e *Store_Expecter) GetTalkgroupsWithLearnedBySystemCount(ctx interface{}, system interface{}) *Store_GetTalkgroupsWithLearnedBySystemCount_Call {
return &Store_GetTalkgroupsWithLearnedBySystemCount_Call{Call: _e.mock.On("GetTalkgroupsWithLearnedBySystemCount", ctx, system)}
// - filter *string
func (_e *Store_Expecter) GetTalkgroupsWithLearnedBySystemCount(ctx interface{}, system interface{}, filter interface{}) *Store_GetTalkgroupsWithLearnedBySystemCount_Call {
return &Store_GetTalkgroupsWithLearnedBySystemCount_Call{Call: _e.mock.On("GetTalkgroupsWithLearnedBySystemCount", ctx, system, filter)}
}
func (_c *Store_GetTalkgroupsWithLearnedBySystemCount_Call) Run(run func(ctx context.Context, system int32)) *Store_GetTalkgroupsWithLearnedBySystemCount_Call {
func (_c *Store_GetTalkgroupsWithLearnedBySystemCount_Call) Run(run func(ctx context.Context, system int32, filter *string)) *Store_GetTalkgroupsWithLearnedBySystemCount_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(int32))
run(args[0].(context.Context), args[1].(int32), args[2].(*string))
})
return _c
}
@ -1868,14 +1869,14 @@ func (_c *Store_GetTalkgroupsWithLearnedBySystemCount_Call) Return(_a0 int64, _a
return _c
}
func (_c *Store_GetTalkgroupsWithLearnedBySystemCount_Call) RunAndReturn(run func(context.Context, int32) (int64, error)) *Store_GetTalkgroupsWithLearnedBySystemCount_Call {
func (_c *Store_GetTalkgroupsWithLearnedBySystemCount_Call) RunAndReturn(run func(context.Context, int32, *string) (int64, error)) *Store_GetTalkgroupsWithLearnedBySystemCount_Call {
_c.Call.Return(run)
return _c
}
// GetTalkgroupsWithLearnedBySystemP provides a mock function with given fields: ctx, system, offset, perPage
func (_m *Store) GetTalkgroupsWithLearnedBySystemP(ctx context.Context, system int32, offset int32, perPage int32) ([]database.GetTalkgroupsWithLearnedBySystemPRow, error) {
ret := _m.Called(ctx, system, offset, perPage)
// GetTalkgroupsWithLearnedBySystemP provides a mock function with given fields: ctx, arg
func (_m *Store) GetTalkgroupsWithLearnedBySystemP(ctx context.Context, arg database.GetTalkgroupsWithLearnedBySystemPParams) ([]database.GetTalkgroupsWithLearnedBySystemPRow, error) {
ret := _m.Called(ctx, arg)
if len(ret) == 0 {
panic("no return value specified for GetTalkgroupsWithLearnedBySystemP")
@ -1883,19 +1884,19 @@ func (_m *Store) GetTalkgroupsWithLearnedBySystemP(ctx context.Context, system i
var r0 []database.GetTalkgroupsWithLearnedBySystemPRow
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, int32, int32, int32) ([]database.GetTalkgroupsWithLearnedBySystemPRow, error)); ok {
return rf(ctx, system, offset, perPage)
if rf, ok := ret.Get(0).(func(context.Context, database.GetTalkgroupsWithLearnedBySystemPParams) ([]database.GetTalkgroupsWithLearnedBySystemPRow, error)); ok {
return rf(ctx, arg)
}
if rf, ok := ret.Get(0).(func(context.Context, int32, int32, int32) []database.GetTalkgroupsWithLearnedBySystemPRow); ok {
r0 = rf(ctx, system, offset, perPage)
if rf, ok := ret.Get(0).(func(context.Context, database.GetTalkgroupsWithLearnedBySystemPParams) []database.GetTalkgroupsWithLearnedBySystemPRow); ok {
r0 = rf(ctx, arg)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]database.GetTalkgroupsWithLearnedBySystemPRow)
}
}
if rf, ok := ret.Get(1).(func(context.Context, int32, int32, int32) error); ok {
r1 = rf(ctx, system, offset, perPage)
if rf, ok := ret.Get(1).(func(context.Context, database.GetTalkgroupsWithLearnedBySystemPParams) error); ok {
r1 = rf(ctx, arg)
} else {
r1 = ret.Error(1)
}
@ -1910,16 +1911,14 @@ type Store_GetTalkgroupsWithLearnedBySystemP_Call struct {
// GetTalkgroupsWithLearnedBySystemP is a helper method to define mock.On call
// - ctx context.Context
// - system int32
// - offset int32
// - perPage int32
func (_e *Store_Expecter) GetTalkgroupsWithLearnedBySystemP(ctx interface{}, system interface{}, offset interface{}, perPage interface{}) *Store_GetTalkgroupsWithLearnedBySystemP_Call {
return &Store_GetTalkgroupsWithLearnedBySystemP_Call{Call: _e.mock.On("GetTalkgroupsWithLearnedBySystemP", ctx, system, offset, perPage)}
// - arg database.GetTalkgroupsWithLearnedBySystemPParams
func (_e *Store_Expecter) GetTalkgroupsWithLearnedBySystemP(ctx interface{}, arg interface{}) *Store_GetTalkgroupsWithLearnedBySystemP_Call {
return &Store_GetTalkgroupsWithLearnedBySystemP_Call{Call: _e.mock.On("GetTalkgroupsWithLearnedBySystemP", ctx, arg)}
}
func (_c *Store_GetTalkgroupsWithLearnedBySystemP_Call) Run(run func(ctx context.Context, system int32, offset int32, perPage int32)) *Store_GetTalkgroupsWithLearnedBySystemP_Call {
func (_c *Store_GetTalkgroupsWithLearnedBySystemP_Call) Run(run func(ctx context.Context, arg database.GetTalkgroupsWithLearnedBySystemPParams)) *Store_GetTalkgroupsWithLearnedBySystemP_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(int32), args[2].(int32), args[3].(int32))
run(args[0].(context.Context), args[1].(database.GetTalkgroupsWithLearnedBySystemPParams))
})
return _c
}
@ -1929,14 +1928,71 @@ func (_c *Store_GetTalkgroupsWithLearnedBySystemP_Call) Return(_a0 []database.Ge
return _c
}
func (_c *Store_GetTalkgroupsWithLearnedBySystemP_Call) RunAndReturn(run func(context.Context, int32, int32, int32) ([]database.GetTalkgroupsWithLearnedBySystemPRow, error)) *Store_GetTalkgroupsWithLearnedBySystemP_Call {
func (_c *Store_GetTalkgroupsWithLearnedBySystemP_Call) RunAndReturn(run func(context.Context, database.GetTalkgroupsWithLearnedBySystemPParams) ([]database.GetTalkgroupsWithLearnedBySystemPRow, error)) *Store_GetTalkgroupsWithLearnedBySystemP_Call {
_c.Call.Return(run)
return _c
}
// GetTalkgroupsWithLearnedP provides a mock function with given fields: ctx, offset, perPage
func (_m *Store) GetTalkgroupsWithLearnedP(ctx context.Context, offset int32, perPage int32) ([]database.GetTalkgroupsWithLearnedPRow, error) {
ret := _m.Called(ctx, offset, perPage)
// GetTalkgroupsWithLearnedCount provides a mock function with given fields: ctx, filter
func (_m *Store) GetTalkgroupsWithLearnedCount(ctx context.Context, filter *string) (int64, error) {
ret := _m.Called(ctx, filter)
if len(ret) == 0 {
panic("no return value specified for GetTalkgroupsWithLearnedCount")
}
var r0 int64
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *string) (int64, error)); ok {
return rf(ctx, filter)
}
if rf, ok := ret.Get(0).(func(context.Context, *string) int64); ok {
r0 = rf(ctx, filter)
} else {
r0 = ret.Get(0).(int64)
}
if rf, ok := ret.Get(1).(func(context.Context, *string) error); ok {
r1 = rf(ctx, filter)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Store_GetTalkgroupsWithLearnedCount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupsWithLearnedCount'
type Store_GetTalkgroupsWithLearnedCount_Call struct {
*mock.Call
}
// GetTalkgroupsWithLearnedCount is a helper method to define mock.On call
// - ctx context.Context
// - filter *string
func (_e *Store_Expecter) GetTalkgroupsWithLearnedCount(ctx interface{}, filter interface{}) *Store_GetTalkgroupsWithLearnedCount_Call {
return &Store_GetTalkgroupsWithLearnedCount_Call{Call: _e.mock.On("GetTalkgroupsWithLearnedCount", ctx, filter)}
}
func (_c *Store_GetTalkgroupsWithLearnedCount_Call) Run(run func(ctx context.Context, filter *string)) *Store_GetTalkgroupsWithLearnedCount_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*string))
})
return _c
}
func (_c *Store_GetTalkgroupsWithLearnedCount_Call) Return(_a0 int64, _a1 error) *Store_GetTalkgroupsWithLearnedCount_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *Store_GetTalkgroupsWithLearnedCount_Call) RunAndReturn(run func(context.Context, *string) (int64, error)) *Store_GetTalkgroupsWithLearnedCount_Call {
_c.Call.Return(run)
return _c
}
// GetTalkgroupsWithLearnedP provides a mock function with given fields: ctx, arg
func (_m *Store) GetTalkgroupsWithLearnedP(ctx context.Context, arg database.GetTalkgroupsWithLearnedPParams) ([]database.GetTalkgroupsWithLearnedPRow, error) {
ret := _m.Called(ctx, arg)
if len(ret) == 0 {
panic("no return value specified for GetTalkgroupsWithLearnedP")
@ -1944,19 +2000,19 @@ func (_m *Store) GetTalkgroupsWithLearnedP(ctx context.Context, offset int32, pe
var r0 []database.GetTalkgroupsWithLearnedPRow
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, int32, int32) ([]database.GetTalkgroupsWithLearnedPRow, error)); ok {
return rf(ctx, offset, perPage)
if rf, ok := ret.Get(0).(func(context.Context, database.GetTalkgroupsWithLearnedPParams) ([]database.GetTalkgroupsWithLearnedPRow, error)); ok {
return rf(ctx, arg)
}
if rf, ok := ret.Get(0).(func(context.Context, int32, int32) []database.GetTalkgroupsWithLearnedPRow); ok {
r0 = rf(ctx, offset, perPage)
if rf, ok := ret.Get(0).(func(context.Context, database.GetTalkgroupsWithLearnedPParams) []database.GetTalkgroupsWithLearnedPRow); ok {
r0 = rf(ctx, arg)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]database.GetTalkgroupsWithLearnedPRow)
}
}
if rf, ok := ret.Get(1).(func(context.Context, int32, int32) error); ok {
r1 = rf(ctx, offset, perPage)
if rf, ok := ret.Get(1).(func(context.Context, database.GetTalkgroupsWithLearnedPParams) error); ok {
r1 = rf(ctx, arg)
} else {
r1 = ret.Error(1)
}
@ -1971,15 +2027,14 @@ type Store_GetTalkgroupsWithLearnedP_Call struct {
// GetTalkgroupsWithLearnedP is a helper method to define mock.On call
// - ctx context.Context
// - offset int32
// - perPage int32
func (_e *Store_Expecter) GetTalkgroupsWithLearnedP(ctx interface{}, offset interface{}, perPage interface{}) *Store_GetTalkgroupsWithLearnedP_Call {
return &Store_GetTalkgroupsWithLearnedP_Call{Call: _e.mock.On("GetTalkgroupsWithLearnedP", ctx, offset, perPage)}
// - arg database.GetTalkgroupsWithLearnedPParams
func (_e *Store_Expecter) GetTalkgroupsWithLearnedP(ctx interface{}, arg interface{}) *Store_GetTalkgroupsWithLearnedP_Call {
return &Store_GetTalkgroupsWithLearnedP_Call{Call: _e.mock.On("GetTalkgroupsWithLearnedP", ctx, arg)}
}
func (_c *Store_GetTalkgroupsWithLearnedP_Call) Run(run func(ctx context.Context, offset int32, perPage int32)) *Store_GetTalkgroupsWithLearnedP_Call {
func (_c *Store_GetTalkgroupsWithLearnedP_Call) Run(run func(ctx context.Context, arg database.GetTalkgroupsWithLearnedPParams)) *Store_GetTalkgroupsWithLearnedP_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(int32), args[2].(int32))
run(args[0].(context.Context), args[1].(database.GetTalkgroupsWithLearnedPParams))
})
return _c
}
@ -1989,63 +2044,7 @@ func (_c *Store_GetTalkgroupsWithLearnedP_Call) Return(_a0 []database.GetTalkgro
return _c
}
func (_c *Store_GetTalkgroupsWithLearnedP_Call) RunAndReturn(run func(context.Context, int32, int32) ([]database.GetTalkgroupsWithLearnedPRow, error)) *Store_GetTalkgroupsWithLearnedP_Call {
_c.Call.Return(run)
return _c
}
// GetTalkgroupsWithLearnedPCount provides a mock function with given fields: ctx
func (_m *Store) GetTalkgroupsWithLearnedPCount(ctx context.Context) (int64, error) {
ret := _m.Called(ctx)
if len(ret) == 0 {
panic("no return value specified for GetTalkgroupsWithLearnedPCount")
}
var r0 int64
var r1 error
if rf, ok := ret.Get(0).(func(context.Context) (int64, error)); ok {
return rf(ctx)
}
if rf, ok := ret.Get(0).(func(context.Context) int64); ok {
r0 = rf(ctx)
} else {
r0 = ret.Get(0).(int64)
}
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(ctx)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Store_GetTalkgroupsWithLearnedPCount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupsWithLearnedPCount'
type Store_GetTalkgroupsWithLearnedPCount_Call struct {
*mock.Call
}
// GetTalkgroupsWithLearnedPCount is a helper method to define mock.On call
// - ctx context.Context
func (_e *Store_Expecter) GetTalkgroupsWithLearnedPCount(ctx interface{}) *Store_GetTalkgroupsWithLearnedPCount_Call {
return &Store_GetTalkgroupsWithLearnedPCount_Call{Call: _e.mock.On("GetTalkgroupsWithLearnedPCount", ctx)}
}
func (_c *Store_GetTalkgroupsWithLearnedPCount_Call) Run(run func(ctx context.Context)) *Store_GetTalkgroupsWithLearnedPCount_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context))
})
return _c
}
func (_c *Store_GetTalkgroupsWithLearnedPCount_Call) Return(_a0 int64, _a1 error) *Store_GetTalkgroupsWithLearnedPCount_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *Store_GetTalkgroupsWithLearnedPCount_Call) RunAndReturn(run func(context.Context) (int64, error)) *Store_GetTalkgroupsWithLearnedPCount_Call {
func (_c *Store_GetTalkgroupsWithLearnedP_Call) RunAndReturn(run func(context.Context, database.GetTalkgroupsWithLearnedPParams) ([]database.GetTalkgroupsWithLearnedPRow, error)) *Store_GetTalkgroupsWithLearnedP_Call {
_c.Call.Return(run)
return _c
}

View file

@ -36,10 +36,10 @@ type Querier interface {
GetTalkgroupsWithAnyTags(ctx context.Context, tags []string) ([]GetTalkgroupsWithAnyTagsRow, error)
GetTalkgroupsWithLearned(ctx context.Context) ([]GetTalkgroupsWithLearnedRow, error)
GetTalkgroupsWithLearnedBySystem(ctx context.Context, system int32) ([]GetTalkgroupsWithLearnedBySystemRow, error)
GetTalkgroupsWithLearnedBySystemCount(ctx context.Context, system int32) (int64, error)
GetTalkgroupsWithLearnedBySystemP(ctx context.Context, system int32, offset int32, perPage int32) ([]GetTalkgroupsWithLearnedBySystemPRow, error)
GetTalkgroupsWithLearnedP(ctx context.Context, offset int32, perPage int32) ([]GetTalkgroupsWithLearnedPRow, error)
GetTalkgroupsWithLearnedPCount(ctx context.Context) (int64, error)
GetTalkgroupsWithLearnedBySystemCount(ctx context.Context, system int32, filter *string) (int64, error)
GetTalkgroupsWithLearnedBySystemP(ctx context.Context, arg GetTalkgroupsWithLearnedBySystemPParams) ([]GetTalkgroupsWithLearnedBySystemPRow, error)
GetTalkgroupsWithLearnedCount(ctx context.Context, filter *string) (int64, error)
GetTalkgroupsWithLearnedP(ctx context.Context, arg GetTalkgroupsWithLearnedPParams) ([]GetTalkgroupsWithLearnedPRow, error)
GetUserByID(ctx context.Context, id int) (User, error)
GetUserByUID(ctx context.Context, id int) (User, error)
GetUserByUsername(ctx context.Context, username string) (User, error)

View file

@ -406,11 +406,17 @@ func (q *Queries) GetTalkgroupsWithLearnedBySystem(ctx context.Context, system i
const getTalkgroupsWithLearnedBySystemCount = `-- name: GetTalkgroupsWithLearnedBySystemCount :one
SELECT COUNT(*) FROM talkgroups tg
WHERE tg.system_id = $1
WHERE tg.system_id = $1 AND
(CASE WHEN $2::TEXT IS NOT NULL THEN (
tg.tg_group ILIKE '%' || $2 || '%' OR
tg.name ILIKE '%' || $2 || '%' OR
tg.alpha_tag ILIKE '%' || $2 || '%' OR
tg.tags @> ARRAY[LOWER($2)]
) ELSE TRUE END)
`
func (q *Queries) GetTalkgroupsWithLearnedBySystemCount(ctx context.Context, system int32) (int64, error) {
row := q.db.QueryRow(ctx, getTalkgroupsWithLearnedBySystemCount, system)
func (q *Queries) GetTalkgroupsWithLearnedBySystemCount(ctx context.Context, system int32, filter *string) (int64, error) {
row := q.db.QueryRow(ctx, getTalkgroupsWithLearnedBySystemCount, system, filter)
var count int64
err := row.Scan(&count)
return count, err
@ -421,19 +427,49 @@ SELECT
tg.id, tg.system_id, tg.tgid, tg.name, tg.alpha_tag, tg.tg_group, tg.frequency, tg.metadata, tg.tags, tg.alert, tg.alert_config, tg.weight, tg.learned, tg.ignored, sys.id, sys.name
FROM talkgroups tg
JOIN systems sys ON tg.system_id = sys.id
WHERE tg.system_id = $1
ORDER BY tg.system_id ASC, tg.tgid ASC
OFFSET $2 ROWS
FETCH NEXT $3 ROWS ONLY
WHERE tg.system_id = $1 AND
(CASE WHEN $2::TEXT IS NOT NULL THEN (
tg.tg_group ILIKE '%' || $2 || '%' OR
tg.name ILIKE '%' || $2 || '%' OR
tg.alpha_tag ILIKE '%' || $2 || '%' OR
tg.tags @> ARRAY[LOWER($2)]
) ELSE TRUE END)
ORDER BY
CASE WHEN $3::TEXT = 'tgid_asc' THEN (tg.system_id, tg.tgid) END ASC,
CASE WHEN $3 = 'tgid_desc' THEN (tg.system_id, tg.tgid) END DESC,
CASE WHEN $3 = 'group_asc' THEN tg.tg_group END ASC,
CASE WHEN $3 = 'group_desc' THEN tg.tg_group END DESC,
CASE WHEN $3 = 'id_asc' THEN tg.id END ASC,
CASE WHEN $3 = 'id_desc' THEN tg.id END DESC,
CASE WHEN $3 = 'name_asc' THEN tg.name END ASC,
CASE WHEN $3 = 'name_desc' THEN tg.name END DESC,
CASE WHEN $3 = 'alpha_asc' THEN tg.alpha_tag END ASC,
CASE WHEN $3 = 'alpha_desc' THEN tg.alpha_tag END DESC
OFFSET $4 ROWS
FETCH NEXT $5 ROWS ONLY
`
type GetTalkgroupsWithLearnedBySystemPParams struct {
System int32 `json:"system"`
Filter *string `json:"filter"`
OrderBy string `json:"order_by"`
Offset int32 `json:"offset"`
PerPage int32 `json:"per_page"`
}
type GetTalkgroupsWithLearnedBySystemPRow struct {
Talkgroup Talkgroup `json:"talkgroup"`
System System `json:"system"`
}
func (q *Queries) GetTalkgroupsWithLearnedBySystemP(ctx context.Context, system int32, offset int32, perPage int32) ([]GetTalkgroupsWithLearnedBySystemPRow, error) {
rows, err := q.db.Query(ctx, getTalkgroupsWithLearnedBySystemP, system, offset, perPage)
func (q *Queries) GetTalkgroupsWithLearnedBySystemP(ctx context.Context, arg GetTalkgroupsWithLearnedBySystemPParams) ([]GetTalkgroupsWithLearnedBySystemPRow, error) {
rows, err := q.db.Query(ctx, getTalkgroupsWithLearnedBySystemP,
arg.System,
arg.Filter,
arg.OrderBy,
arg.Offset,
arg.PerPage,
)
if err != nil {
return nil, err
}
@ -469,24 +505,70 @@ func (q *Queries) GetTalkgroupsWithLearnedBySystemP(ctx context.Context, system
return items, nil
}
const getTalkgroupsWithLearnedCount = `-- name: GetTalkgroupsWithLearnedCount :one
SELECT COUNT(*) FROM talkgroups tg
WHERE ignored IS NOT TRUE AND
(CASE WHEN $1::TEXT IS NOT NULL THEN (
tg.tg_group ILIKE '%' || $1 || '%' OR
tg.name ILIKE '%' || $1 || '%' OR
tg.alpha_tag ILIKE '%' || $1 || '%' OR
tg.tags @> ARRAY[LOWER($1)]
) ELSE TRUE END)
`
func (q *Queries) GetTalkgroupsWithLearnedCount(ctx context.Context, filter *string) (int64, error) {
row := q.db.QueryRow(ctx, getTalkgroupsWithLearnedCount, filter)
var count int64
err := row.Scan(&count)
return count, err
}
const getTalkgroupsWithLearnedP = `-- name: GetTalkgroupsWithLearnedP :many
SELECT
tg.id, tg.system_id, tg.tgid, tg.name, tg.alpha_tag, tg.tg_group, tg.frequency, tg.metadata, tg.tags, tg.alert, tg.alert_config, tg.weight, tg.learned, tg.ignored, sys.id, sys.name
FROM talkgroups tg
JOIN systems sys ON tg.system_id = sys.id
WHERE ignored IS NOT TRUE
ORDER BY tg.system_id ASC, tg.tgid ASC
OFFSET $1 ROWS
FETCH NEXT $2 ROWS ONLY
WHERE ignored IS NOT TRUE AND
(CASE WHEN $1::TEXT IS NOT NULL THEN (
tg.tg_group ILIKE '%' || $1 || '%' OR
tg.name ILIKE '%' || $1 || '%' OR
tg.alpha_tag ILIKE '%' || $1 || '%' OR
tg.tags @> ARRAY[LOWER($1)]
) ELSE TRUE END)
ORDER BY
CASE WHEN $2::TEXT = 'tgid_asc' THEN (tg.system_id, tg.tgid) END ASC,
CASE WHEN $2 = 'tgid_desc' THEN (tg.system_id, tg.tgid) END DESC,
CASE WHEN $2 = 'group_asc' THEN tg.tg_group END ASC,
CASE WHEN $2 = 'group_desc' THEN tg.tg_group END DESC,
CASE WHEN $2 = 'id_asc' THEN tg.id END ASC,
CASE WHEN $2 = 'id_desc' THEN tg.id END DESC,
CASE WHEN $2 = 'name_asc' THEN tg.name END ASC,
CASE WHEN $2 = 'name_desc' THEN tg.name END DESC,
CASE WHEN $2 = 'alpha_asc' THEN tg.alpha_tag END ASC,
CASE WHEN $2 = 'alpha_desc' THEN tg.alpha_tag END DESC
OFFSET $3 ROWS
FETCH NEXT $4 ROWS ONLY
`
type GetTalkgroupsWithLearnedPParams struct {
Filter *string `json:"filter"`
OrderBy string `json:"order_by"`
Offset int32 `json:"offset"`
PerPage int32 `json:"per_page"`
}
type GetTalkgroupsWithLearnedPRow struct {
Talkgroup Talkgroup `json:"talkgroup"`
System System `json:"system"`
}
func (q *Queries) GetTalkgroupsWithLearnedP(ctx context.Context, offset int32, perPage int32) ([]GetTalkgroupsWithLearnedPRow, error) {
rows, err := q.db.Query(ctx, getTalkgroupsWithLearnedP, offset, perPage)
func (q *Queries) GetTalkgroupsWithLearnedP(ctx context.Context, arg GetTalkgroupsWithLearnedPParams) ([]GetTalkgroupsWithLearnedPRow, error) {
rows, err := q.db.Query(ctx, getTalkgroupsWithLearnedP,
arg.Filter,
arg.OrderBy,
arg.Offset,
arg.PerPage,
)
if err != nil {
return nil, err
}
@ -522,18 +604,6 @@ func (q *Queries) GetTalkgroupsWithLearnedP(ctx context.Context, offset int32, p
return items, nil
}
const getTalkgroupsWithLearnedPCount = `-- name: GetTalkgroupsWithLearnedPCount :one
SELECT COUNT(*) FROM talkgroups tg
WHERE ignored IS NOT TRUE
`
func (q *Queries) GetTalkgroupsWithLearnedPCount(ctx context.Context) (int64, error) {
row := q.db.QueryRow(ctx, getTalkgroupsWithLearnedPCount)
var count int64
err := row.Scan(&count)
return count, err
}
const restoreTalkgroupVersion = `-- name: RestoreTalkgroupVersion :one
INSERT INTO talkgroups(
system_id,

View file

@ -115,6 +115,8 @@ var statusMapping = map[error]errResponder{
tgstore.ErrNoSuchSystem: notFoundErrText,
tgstore.ErrNotFound: notFoundErrText,
tgstore.ErrInvalidOrderBy: badRequestErrText,
tgstore.ErrBadDirection: badRequestErrText,
tgstore.ErrBadOrder: badRequestErrText,
pgx.ErrNoRows: recordNotFound,
ErrMissingTGSys: badRequestErrText,
ErrTGIDMismatch: badRequestErrText,

View file

@ -106,6 +106,12 @@ func (tga *talkgroupAPI) get(w http.ResponseWriter, r *http.Request) {
respond(w, r, res)
}
type FilterPagination struct {
tgstore.Pagination
Filter *string `json:"filter"`
}
func (tga *talkgroupAPI) postPaginated(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
tgs := tgstore.FromCtx(ctx)
@ -118,7 +124,7 @@ func (tga *talkgroupAPI) postPaginated(w http.ResponseWriter, r *http.Request) {
return
}
input := &tgstore.Pagination{}
input := &FilterPagination{}
err = forms.Unmarshal(r, input, forms.WithTag("json"), forms.WithAcceptBlank(), forms.WithOmitEmpty())
if err != nil {
wErr(w, r, badRequest(err))
@ -129,12 +135,18 @@ func (tga *talkgroupAPI) postPaginated(w http.ResponseWriter, r *http.Request) {
Talkgroups []*talkgroups.Talkgroup `json:"talkgroups"`
Count int `json:"count"`
}{}
opts := []tgstore.Option{
tgstore.WithPagination(&input.Pagination, DefaultPerPage, &res.Count),
tgstore.WithFilter(input.Filter),
}
switch {
case p.System != nil:
res.Talkgroups, err = tgs.SystemTGs(ctx, *p.System, tgstore.WithPagination(input, DefaultPerPage, &res.Count))
res.Talkgroups, err = tgs.SystemTGs(ctx, *p.System, opts...)
default:
// get all talkgroups
res.Talkgroups, err = tgs.TGs(ctx, nil, tgstore.WithPagination(input, DefaultPerPage, &res.Count))
res.Talkgroups, err = tgs.TGs(ctx, nil, opts...)
}
if err != nil {

View file

@ -25,6 +25,8 @@ var (
ErrNoSuchSystem = errors.New("no such system")
ErrInvalidOrderBy = errors.New("invalid pagination orderBy value")
ErrReference = errors.New("item is still referenced, cannot delete")
ErrBadOrder = errors.New("invalid order")
ErrBadDirection = errors.New("invalid direction")
)
type Store interface {
@ -41,13 +43,13 @@ type Store interface {
TG(ctx context.Context, tg tgsp.ID) (*tgsp.Talkgroup, error)
// TGs retrieves many talkgroups from the Store.
TGs(ctx context.Context, tgs tgsp.IDs, opts ...option) ([]*tgsp.Talkgroup, error)
TGs(ctx context.Context, tgs tgsp.IDs, opts ...Option) ([]*tgsp.Talkgroup, error)
// LearnTG learns the talkgroup from a Call.
LearnTG(ctx context.Context, call *calls.Call) (*tgsp.Talkgroup, error)
// SystemTGs retrieves all Talkgroups associated with a System.
SystemTGs(ctx context.Context, systemID int, opts ...option) ([]*tgsp.Talkgroup, error)
SystemTGs(ctx context.Context, systemID int, opts ...Option) ([]*tgsp.Talkgroup, error)
// DeleteTG deletes a talkgroup record.
DeleteTG(ctx context.Context, id tgsp.ID) error
@ -78,18 +80,20 @@ type options struct {
pagination *Pagination
totalDest *int
perPageDefault int
filter *string
}
func sOpt(opts []option) (o options) {
func sOpt(opts []Option) (o options) {
for _, opt := range opts {
opt(&o)
}
return
}
type option func(*options)
type Option func(*options)
func WithPagination(p *Pagination, defPerPage int, totalDest *int) option {
func WithPagination(p *Pagination, defPerPage int, totalDest *int) Option {
return func(o *options) {
o.pagination = p
o.perPageDefault = defPerPage
@ -97,22 +101,72 @@ func WithPagination(p *Pagination, defPerPage int, totalDest *int) option {
}
}
func (p *Pagination) SortDir() (string, error) {
order := TGOrderTGID
dir := TGDirAsc
if p != nil {
if p.OrderBy != nil {
if !p.OrderBy.IsValid() {
return "", ErrBadOrder
}
order = *p.OrderBy
}
if p.Direction != nil {
if !p.Direction.IsValid() {
return "", ErrBadDirection
}
dir = *p.Direction
}
}
return string(order) + "_" + string(dir), nil
}
func WithFilter(f *string) Option {
return func(o *options) {
o.filter = f
}
}
type TGOrder string
type TGDirection string
const (
TGOrderID TGOrder = "id"
TGOrderTGID TGOrder = "tgid"
TGOrderGroup TGOrder = "group"
TGOrderName TGOrder = "name"
TGOrderID TGOrder = "id"
TGOrderAlpha TGOrder = "alpha"
TGDirAsc TGDirection = "asc"
TGDirDesc TGDirection = "desc"
)
func (t *TGDirection) IsValid() bool {
if t == nil {
return true
}
switch *t {
case TGDirAsc, TGDirDesc:
return true
}
return false
}
func (t *TGOrder) IsValid() bool {
if t == nil {
return true
}
switch *t {
case TGOrderTGID, TGOrderGroup, TGOrderName, TGOrderID:
case TGOrderID, TGOrderTGID, TGOrderGroup, TGOrderName, TGOrderAlpha:
return true
}
@ -122,7 +176,8 @@ func (t *TGOrder) IsValid() bool {
type Pagination struct {
common.Pagination
OrderBy *TGOrder `json:"orderBy"`
OrderBy *TGOrder `json:"orderBy"`
Direction *TGDirection `json:"dir"`
}
type storeCtxKey string
@ -196,7 +251,7 @@ func (t *cache) Hint(ctx context.Context, tgs []tgsp.ID) error {
}
}
if len(toLoad) > 0 {
if len(toLoad[0]) > 0 {
t.RUnlock()
return t.Load(ctx, toLoad)
}
@ -281,8 +336,9 @@ func addToRowList[T rowType](t *cache, tgRecords []T) []*tgsp.Talkgroup {
return r
}
func (t *cache) TGs(ctx context.Context, tgs tgsp.IDs, opts ...option) ([]*tgsp.Talkgroup, error) {
func (t *cache) TGs(ctx context.Context, tgs tgsp.IDs, opts ...Option) ([]*tgsp.Talkgroup, error) {
db := database.FromCtx(ctx)
r := make([]*tgsp.Talkgroup, 0, len(tgs))
opt := sOpt(opts)
var err error
@ -307,17 +363,27 @@ func (t *cache) TGs(ctx context.Context, tgs tgsp.IDs, opts ...option) ([]*tgsp.
// get all talkgroups
if opt.pagination != nil {
sortDir, err := opt.pagination.SortDir()
if err != nil {
return nil, err
}
offset, perPage := opt.pagination.OffsetPerPage(opt.perPageDefault)
var tgRecords []database.GetTalkgroupsWithLearnedPRow
var err error
err = db.InTx(ctx, func(db database.Store) error {
tgRecords, err = db.GetTalkgroupsWithLearnedP(ctx, offset, perPage)
var err error
tgRecords, err = db.GetTalkgroupsWithLearnedP(ctx, database.GetTalkgroupsWithLearnedPParams{
Filter: opt.filter,
OrderBy: sortDir,
Offset: offset,
PerPage: perPage,
})
if err != nil {
return err
}
if opt.totalDest != nil {
count, err := db.GetTalkgroupsWithLearnedPCount(ctx)
count, err := db.GetTalkgroupsWithLearnedCount(ctx, opt.filter)
if err != nil {
return err
}
@ -368,16 +434,47 @@ func (t *cache) Weight(ctx context.Context, id tgsp.ID, tm time.Time) float64 {
return float64(m)
}
func (t *cache) SystemTGs(ctx context.Context, systemID int, opts ...option) ([]*tgsp.Talkgroup, error) {
func (t *cache) SystemTGs(ctx context.Context, systemID int, opts ...Option) ([]*tgsp.Talkgroup, error) {
db := database.FromCtx(ctx)
opt := sOpt(opts)
var err error
if opt.pagination != nil {
offset, perPage := opt.pagination.OffsetPerPage(opt.perPageDefault)
recs, err := db.GetTalkgroupsWithLearnedBySystemP(ctx, int32(systemID), offset, perPage)
sortDir, err := opt.pagination.SortDir()
if err != nil {
return nil, err
}
offset, perPage := opt.pagination.OffsetPerPage(opt.perPageDefault)
var recs []database.GetTalkgroupsWithLearnedBySystemPRow
err = db.InTx(ctx, func(db database.Store) error {
var err error
recs, err = db.GetTalkgroupsWithLearnedBySystemP(ctx, database.GetTalkgroupsWithLearnedBySystemPParams{
System: int32(systemID),
Filter: opt.filter,
OrderBy: sortDir,
Offset: offset,
PerPage: perPage,
})
if err != nil {
return err
}
if opt.totalDest != nil {
count, err := db.GetTalkgroupsWithLearnedBySystemCount(ctx, int32(systemID), opt.filter)
if err != nil {
return err
}
*opt.totalDest = int(count)
}
return nil
}, pgx.TxOptions{})
if err != nil {
return nil, err
}
return addToRowList(t, recs), nil
}

View file

@ -36,14 +36,38 @@ SELECT
sqlc.embed(tg), sqlc.embed(sys)
FROM talkgroups tg
JOIN systems sys ON tg.system_id = sys.id
WHERE tg.system_id = @system
ORDER BY tg.system_id ASC, tg.tgid ASC
WHERE tg.system_id = @system AND
(CASE WHEN sqlc.narg('filter')::TEXT IS NOT NULL THEN (
tg.tg_group ILIKE '%' || @filter || '%' OR
tg.name ILIKE '%' || @filter || '%' OR
tg.alpha_tag ILIKE '%' || @filter || '%' OR
tg.tags @> ARRAY[LOWER(@filter)]
) ELSE TRUE END)
ORDER BY
CASE WHEN @order_by::TEXT = 'tgid_asc' THEN (tg.system_id, tg.tgid) END ASC,
CASE WHEN @order_by = 'tgid_desc' THEN (tg.system_id, tg.tgid) END DESC,
CASE WHEN @order_by = 'group_asc' THEN tg.tg_group END ASC,
CASE WHEN @order_by = 'group_desc' THEN tg.tg_group END DESC,
CASE WHEN @order_by = 'id_asc' THEN tg.id END ASC,
CASE WHEN @order_by = 'id_desc' THEN tg.id END DESC,
CASE WHEN @order_by = 'name_asc' THEN tg.name END ASC,
CASE WHEN @order_by = 'name_desc' THEN tg.name END DESC,
CASE WHEN @order_by = 'alpha_asc' THEN tg.alpha_tag END ASC,
CASE WHEN @order_by = 'alpha_desc' THEN tg.alpha_tag END DESC
OFFSET sqlc.arg('offset') ROWS
FETCH NEXT sqlc.arg('per_page') ROWS ONLY;
FETCH NEXT sqlc.arg('per_page') ROWS ONLY
;
-- name: GetTalkgroupsWithLearnedBySystemCount :one
SELECT COUNT(*) FROM talkgroups tg
WHERE tg.system_id = @system;
WHERE tg.system_id = @system AND
(CASE WHEN sqlc.narg('filter')::TEXT IS NOT NULL THEN (
tg.tg_group ILIKE '%' || @filter || '%' OR
tg.name ILIKE '%' || @filter || '%' OR
tg.alpha_tag ILIKE '%' || @filter || '%' OR
tg.tags @> ARRAY[LOWER(@filter)]
) ELSE TRUE END)
;
-- name: GetTalkgroupsWithLearnedBySystem :many
SELECT
@ -64,14 +88,38 @@ SELECT
sqlc.embed(tg), sqlc.embed(sys)
FROM talkgroups tg
JOIN systems sys ON tg.system_id = sys.id
WHERE ignored IS NOT TRUE
ORDER BY tg.system_id ASC, tg.tgid ASC
WHERE ignored IS NOT TRUE AND
(CASE WHEN sqlc.narg('filter')::TEXT IS NOT NULL THEN (
tg.tg_group ILIKE '%' || @filter || '%' OR
tg.name ILIKE '%' || @filter || '%' OR
tg.alpha_tag ILIKE '%' || @filter || '%' OR
tg.tags @> ARRAY[LOWER(@filter)]
) ELSE TRUE END)
ORDER BY
CASE WHEN @order_by::TEXT = 'tgid_asc' THEN (tg.system_id, tg.tgid) END ASC,
CASE WHEN @order_by = 'tgid_desc' THEN (tg.system_id, tg.tgid) END DESC,
CASE WHEN @order_by = 'group_asc' THEN tg.tg_group END ASC,
CASE WHEN @order_by = 'group_desc' THEN tg.tg_group END DESC,
CASE WHEN @order_by = 'id_asc' THEN tg.id END ASC,
CASE WHEN @order_by = 'id_desc' THEN tg.id END DESC,
CASE WHEN @order_by = 'name_asc' THEN tg.name END ASC,
CASE WHEN @order_by = 'name_desc' THEN tg.name END DESC,
CASE WHEN @order_by = 'alpha_asc' THEN tg.alpha_tag END ASC,
CASE WHEN @order_by = 'alpha_desc' THEN tg.alpha_tag END DESC
OFFSET sqlc.arg('offset') ROWS
FETCH NEXT sqlc.arg('per_page') ROWS ONLY;
FETCH NEXT sqlc.arg('per_page') ROWS ONLY
;
-- name: GetTalkgroupsWithLearnedPCount :one
-- name: GetTalkgroupsWithLearnedCount :one
SELECT COUNT(*) FROM talkgroups tg
WHERE ignored IS NOT TRUE;
WHERE ignored IS NOT TRUE AND
(CASE WHEN sqlc.narg('filter')::TEXT IS NOT NULL THEN (
tg.tg_group ILIKE '%' || @filter || '%' OR
tg.name ILIKE '%' || @filter || '%' OR
tg.alpha_tag ILIKE '%' || @filter || '%' OR
tg.tags @> ARRAY[LOWER(@filter)]
) ELSE TRUE END)
;
-- name: GetSystemName :one
SELECT name FROM systems WHERE id = @system_id;