added option to call Dao.RecordQuery() with the collection id or name

This commit is contained in:
Gani Georgiev 2023-07-13 22:38:55 +03:00
parent a38bd5bedc
commit fdccdcebad
4 changed files with 127 additions and 68 deletions

View File

@ -77,6 +77,9 @@
- Minor Admin UI fixes (typos, grammar fixes, removed unnecessary 404 error check, etc.). - Minor Admin UI fixes (typos, grammar fixes, removed unnecessary 404 error check, etc.).
- (@todo docs) For consistency and convenience it is now possible to call `Dao.RecordQuery(collectionModelOrIdentifier)` with just the collection id or name.
In case an invalid collection id/name string is passed the query will be resolved with cancelled context error.
## v0.16.8 ## v0.16.8

View File

@ -1,6 +1,7 @@
package daos package daos
import ( import (
"context"
"database/sql" "database/sql"
"errors" "errors"
"fmt" "fmt"
@ -18,16 +19,51 @@ import (
"github.com/spf13/cast" "github.com/spf13/cast"
) )
// RecordQuery returns a new Record select query. // RecordQuery returns a new Record select query from a collection model, id or name.
func (dao *Dao) RecordQuery(collection *models.Collection) *dbx.SelectQuery { //
tableName := collection.Name // In case a collection id or name is provided and that collection doesn't
// actually exists, the generated query will be created with a cancelled context
// and will fail once an executor (Row(), One(), All(), etc.) is called.
func (dao *Dao) RecordQuery(collectionModelOrIdentifier any) *dbx.SelectQuery {
var tableName string
var collection *models.Collection
var collectionErr error
switch c := collectionModelOrIdentifier.(type) {
case *models.Collection:
collection = c
tableName = collection.Name
case models.Collection:
collection = &c
tableName = collection.Name
case string:
collection, collectionErr = dao.FindCollectionByNameOrId(c)
if collection != nil {
tableName = collection.Name
} else {
// update with some fake table name for easier debugging
tableName = "@@__missing_" + c
}
default:
// update with some fake table name for easier debugging
tableName = "@@__invalidCollectionModelOrIdentifier"
collectionErr = errors.New("unsupported collection identifier, must be collection model, id or name")
}
selectCols := fmt.Sprintf("%s.*", dao.DB().QuoteSimpleColumnName(tableName)) selectCols := fmt.Sprintf("%s.*", dao.DB().QuoteSimpleColumnName(tableName))
return dao.DB(). query := dao.DB().Select(selectCols).From(tableName)
Select(selectCols).
From(tableName). // in case of an error attach a new context and cancel it immediately with the error
WithBuildHook(func(query *dbx.Query) { if collectionErr != nil {
query.WithExecHook(execLockRetry(dao.ModelQueryTimeout, dao.MaxLockRetries)). // @todo consider changing to WithCancelCause when upgrading
// the min Go requirement to 1.20, so that we can pass the error
ctx, cancelFunc := context.WithCancel(context.Background())
query.WithContext(ctx)
cancelFunc()
}
return query.WithBuildHook(func(q *dbx.Query) {
q.WithExecHook(execLockRetry(dao.ModelQueryTimeout, dao.MaxLockRetries)).
WithOneHook(func(q *dbx.Query, a any, op func(b any) error) error { WithOneHook(func(q *dbx.Query, a any, op func(b any) error) error {
switch v := a.(type) { switch v := a.(type) {
case *models.Record: case *models.Record:

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"database/sql" "database/sql"
"errors" "errors"
"fmt"
"regexp" "regexp"
"strings" "strings"
"testing" "testing"
@ -19,7 +18,7 @@ import (
"github.com/pocketbase/pocketbase/tools/types" "github.com/pocketbase/pocketbase/tools/types"
) )
func TestRecordQuery(t *testing.T) { func TestRecordQueryWithDifferentCollectionValues(t *testing.T) {
app, _ := tests.NewTestApp() app, _ := tests.NewTestApp()
defer app.Cleanup() defer app.Cleanup()
@ -28,11 +27,33 @@ func TestRecordQuery(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
expected := fmt.Sprintf("SELECT `%s`.* FROM `%s`", collection.Name, collection.Name) scenarios := []struct {
name any
collection any
expectedTotal int
expectError bool
}{
{"with nil value", nil, 0, true},
{"with invalid or missing collection id/name", "missing", 0, true},
{"with pointer model", collection, 3, false},
{"with value model", *collection, 3, false},
{"with name", "demo1", 3, false},
{"with id", "wsmn24bux7wo113", 3, false},
}
sql := app.Dao().RecordQuery(collection).Build().SQL() for _, s := range scenarios {
if sql != expected { var records []*models.Record
t.Errorf("Expected sql %s, got %s", expected, sql) err := app.Dao().RecordQuery(s.collection).All(&records)
hasErr := err != nil
if hasErr != s.expectError {
t.Errorf("[%s] Expected hasError %v, got %v", s.name, s.expectError, hasErr)
continue
}
if total := len(records); total != s.expectedTotal {
t.Errorf("[%s] Expected %d records, got %d", s.name, s.expectedTotal, total)
}
} }
} }

View File

@ -27,7 +27,6 @@ func SubtractSlice[T comparable](base []T, subtract []T) []T {
// ExistInSlice checks whether a comparable element exists in a slice of the same type. // ExistInSlice checks whether a comparable element exists in a slice of the same type.
func ExistInSlice[T comparable](item T, list []T) bool { func ExistInSlice[T comparable](item T, list []T) bool {
for _, v := range list { for _, v := range list {
if v == item { if v == item {
return true return true