diff --git a/compose/internal/repository/record.go b/compose/internal/repository/record.go index 19bcc3501..4a17549af 100644 --- a/compose/internal/repository/record.go +++ b/compose/internal/repository/record.go @@ -11,8 +11,8 @@ import ( "gopkg.in/Masterminds/squirrel.v1" "github.com/cortezaproject/corteza-server/compose/internal/repository/ql" - "github.com/cortezaproject/corteza-server/compose/types" + "github.com/cortezaproject/corteza-server/pkg/rh" ) type ( @@ -117,7 +117,7 @@ func (r record) Report(module *types.Module, metrics, dimensions, filter string) func (r record) Find(module *types.Module, filter types.RecordFilter) (set types.RecordSet, f types.RecordFilter, err error) { var query squirrel.SelectBuilder f = filter - f.PerPage = normalizePerPage(f.PerPage, 5, 100, 50) + f.PerPage = rh.NormalizePerPage(f.PerPage, 5, 100, 50) query, err = r.buildQuery(module, filter) if err != nil { diff --git a/compose/internal/repository/repository.go b/compose/internal/repository/repository.go index 6a3f06d67..85679423a 100644 --- a/compose/internal/repository/repository.go +++ b/compose/internal/repository/repository.go @@ -3,12 +3,11 @@ package repository import ( "context" - "github.com/lann/builder" - "github.com/pkg/errors" "github.com/titpetric/factory" "gopkg.in/Masterminds/squirrel.v1" "github.com/cortezaproject/corteza-server/internal/auth" + "github.com/cortezaproject/corteza-server/pkg/rh" ) type ( @@ -51,82 +50,23 @@ func (r *repository) db() *factory.DB { // Fetches single row from table func (r repository) fetchOne(one interface{}, q squirrel.SelectBuilder) (err error) { - var ( - sql string - args []interface{} - ) - - if sql, args, err = q.ToSql(); err != nil { - return - } - - if err = r.db().Get(one, sql, args...); err != nil { - return - } - - return + return rh.FetchOne(r.db(), q, one) } // Counts all rows that match conditions from given query builder func (r repository) count(q squirrel.SelectBuilder) (uint, error) { - var ( - count uint - cq = q.Column("COUNT(*)") - ) - - // Remove order-bys for counting - cq = builder.Delete(cq, "OrderBys").(squirrel.SelectBuilder) - - if sqlSelect, argsSelect, err := cq.ToSql(); err != nil { - return 0, err - } else { - if err := r.db().Get(&count, sqlSelect, argsSelect...); err != nil { - return 0, err - } - } - - return count, nil + return rh.Count(r.db(), q) } // Fetches paged rows func (r repository) fetchPaged(set interface{}, q squirrel.SelectBuilder, page, perPage uint) error { - if perPage > 0 { - q = q.Limit(uint64(perPage)) - } - - if page > 0 { - q = q.Offset(uint64(page * perPage)) - } - - if sqlSelect, argsSelect, err := q.ToSql(); err != nil { - return err - } else { - return r.db().Select(set, sqlSelect, argsSelect...) - } + return rh.FetchPaged(r.db(), q, page, perPage, set) } func normalizePerPage(val, min, max, def uint) uint { - if val == 0 { - return def - } - - if max > 0 && val > max { - return max - } - - if min > 0 && val < min { - return min - } - - return val + return rh.NormalizePerPage(val, min, max, def) } func isFound(err error, valid bool, nerr error) error { - if err != nil { - return errors.WithStack(err) - } else if !valid { - return errors.WithStack(nerr) - } - - return nil + return rh.IsFound(err, valid, nerr) } diff --git a/pkg/rh/README.md b/pkg/rh/README.md new file mode 100644 index 000000000..9885923e0 --- /dev/null +++ b/pkg/rh/README.md @@ -0,0 +1,4 @@ +# (rh) Repository Helpers + +Package name is short an abbreviated to avoid +colliding with per-service repository package names. diff --git a/pkg/rh/helpers.go b/pkg/rh/helpers.go new file mode 100644 index 000000000..5904935af --- /dev/null +++ b/pkg/rh/helpers.go @@ -0,0 +1,99 @@ +package rh + +import ( + "github.com/lann/builder" + "github.com/pkg/errors" + "github.com/titpetric/factory" + "gopkg.in/Masterminds/squirrel.v1" +) + +// FetchOne fetches single row from table +func FetchOne(db *factory.DB, q squirrel.SelectBuilder, one interface{}) (err error) { + var ( + sql string + args []interface{} + ) + + if sql, args, err = q.ToSql(); err != nil { + return + } + + if err = db.Get(one, sql, args...); err != nil { + return + } + + return +} + +// Count counts all rows that match conditions from given query builder +func Count(db *factory.DB, q squirrel.SelectBuilder) (count uint, err error) { + // Remove order-bys for counting + q = builder.Delete(q, "OrderBys").(squirrel.SelectBuilder) + + // Replace columns + q = builder.Delete(q, "Columns").(squirrel.SelectBuilder).Column("COUNT(*)") + + if sqlSelect, argsSelect, err := q.ToSql(); err != nil { + return 0, err + } else { + if err := db.Get(&count, sqlSelect, argsSelect...); err != nil { + return 0, err + } + } + + return count, nil +} + +// FetchPaged fetches paged rows +func FetchPaged(db *factory.DB, q squirrel.SelectBuilder, page, perPage uint, set interface{}) error { + if perPage > 0 { + q = q.Limit(uint64(perPage)) + } + + if page > 0 { + q = q.Offset(uint64(page * perPage)) + } + + if sqlSelect, argsSelect, err := q.ToSql(); err != nil { + return err + } else { + return db.Select(set, sqlSelect, argsSelect...) + } +} + +// FetchPaged fetches paged rows +func FetchAll(db *factory.DB, q squirrel.SelectBuilder, set interface{}) error { + if sqlSelect, argsSelect, err := q.ToSql(); err != nil { + return err + } else { + return db.Select(set, sqlSelect, argsSelect...) + } +} + +// NormalizePerPage normalize page number +func NormalizePerPage(val, min, max, def uint) uint { + if val == 0 { + return def + } + + if max > 0 && val > max { + return max + } + + if min > 0 && val < min { + return min + } + + return val +} + +// IsFound helps with one-row results +func IsFound(err error, valid bool, nerr error) error { + if err != nil { + return errors.WithStack(err) + } else if !valid { + return errors.WithStack(nerr) + } + + return nil +} diff --git a/pkg/rh/paging.go b/pkg/rh/paging.go new file mode 100644 index 000000000..f6e502ca5 --- /dev/null +++ b/pkg/rh/paging.go @@ -0,0 +1,30 @@ +package rh + +const ( + PER_PAGE_DEFAULT uint = 50 + PER_PAGE_MAX = 200 + PER_PAGE_MIN = 10 +) + +type ( + PageFilter struct { + Page uint `json:"page"` + PerPage uint `json:"perPage"` + Count uint `json:"count"` + } +) + +func Paging(page, perPage uint) PageFilter { + return PageFilter{ + Page: page, + PerPage: perPage, + } +} + +func (pf *PageFilter) NormalizePerPage(min, max, def uint) { + pf.PerPage = NormalizePerPage(pf.PerPage, min, max, def) +} + +func (pf *PageFilter) NormalizePerPageWithDefaults() { + pf.PerPage = NormalizePerPage(pf.PerPage, PER_PAGE_MIN, PER_PAGE_MAX, PER_PAGE_DEFAULT) +}