Introduce helper func(IteratorPaging) to count total and create page navigation for records and also fixes timezone and milliseconds fir dateTime field value for query.
187 lines
3.4 KiB
Go
187 lines
3.4 KiB
Go
package dal
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/cortezaproject/corteza-server/pkg/filter"
|
|
"io"
|
|
)
|
|
|
|
type (
|
|
// Iterator provides an interface for loading data from the underlying store
|
|
Iterator interface {
|
|
Next(ctx context.Context) bool
|
|
More(uint, ValueGetter) error
|
|
Err() error
|
|
Scan(ValueSetter) error
|
|
Close() error
|
|
|
|
BackCursor(ValueGetter) (*filter.PagingCursor, error)
|
|
ForwardCursor(ValueGetter) (*filter.PagingCursor, error)
|
|
|
|
// // -1 means unknown
|
|
// Total() int
|
|
// Cursor() any
|
|
// // ... do we need anything else here?
|
|
}
|
|
)
|
|
|
|
// IteratorEncodeJSON helper function that encodes each item from the iterator as JSON
|
|
// and writes it to th given io.Writer.
|
|
//
|
|
// target initialization function is intentionally used to avoid use of reflection
|
|
func IteratorEncodeJSON(ctx context.Context, w io.Writer, iter Iterator, initTarget func() ValueSetter) (err error) {
|
|
var (
|
|
target ValueSetter
|
|
firstOut = false
|
|
)
|
|
|
|
for iter.Next(ctx) {
|
|
if err = iter.Err(); err != nil {
|
|
return
|
|
}
|
|
|
|
if firstOut {
|
|
if _, err = w.Write([]byte(`,`)); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
firstOut = true
|
|
|
|
target = initTarget()
|
|
|
|
if err = iter.Scan(target); err != nil {
|
|
return
|
|
}
|
|
|
|
err = json.NewEncoder(w).Encode(target)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// IteratorPaging helper function for record paging cursor and total
|
|
func IteratorPaging(ctx context.Context, iter Iterator, pp *filter.Paging, ss filter.Sorting, fn func(i Iterator) (ValueGetter, bool)) (err error) {
|
|
if pp == nil {
|
|
return
|
|
}
|
|
|
|
if pp.PageCursor != nil {
|
|
if pp.IncPageNavigation || pp.IncTotal {
|
|
return fmt.Errorf("not allowed to fetch page navigation or total item count with page cursor")
|
|
}
|
|
}
|
|
|
|
pp.Total = 0
|
|
pp.PrevPage = nil
|
|
pp.NextPage = nil
|
|
pp.PageNavigation = []*filter.Page{}
|
|
|
|
const howMuchMore = 1000
|
|
|
|
var (
|
|
counter uint
|
|
total uint
|
|
fetchMore bool
|
|
|
|
cur *filter.PagingCursor
|
|
page = filter.Page{
|
|
Page: 1,
|
|
Count: 0,
|
|
Cursor: nil,
|
|
}
|
|
)
|
|
|
|
for iter.Next(ctx) {
|
|
if err = iter.Err(); err != nil {
|
|
return
|
|
}
|
|
|
|
r, ok := fn(iter)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
counter++
|
|
total++
|
|
page.Count++
|
|
|
|
if pp.PrevPage == nil {
|
|
pp.PrevPage, err = iter.BackCursor(r)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
// cursor for each page
|
|
cur, err = iter.ForwardCursor(r)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if total%pp.Limit == 0 {
|
|
pp.PageNavigation = append(pp.PageNavigation, &filter.Page{
|
|
Page: page.Page,
|
|
Count: page.Count,
|
|
Cursor: page.Cursor,
|
|
})
|
|
|
|
// prep next page
|
|
page = filter.Page{
|
|
Page: uint(len(pp.PageNavigation) + 1),
|
|
Count: 0,
|
|
Cursor: cur,
|
|
}
|
|
|
|
// fetch more records
|
|
fetchMore = true
|
|
|
|
if pp.NextPage == nil {
|
|
pp.NextPage = cur
|
|
}
|
|
}
|
|
|
|
if (len(pp.PageNavigation) == 1 && fetchMore) || counter == howMuchMore {
|
|
counter = 0
|
|
fetchMore = false
|
|
|
|
// request more items
|
|
if err = iter.More(howMuchMore, r); err != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// push the last page to page navigation
|
|
if page.Count > 0 {
|
|
pp.PageNavigation = append(pp.PageNavigation, &filter.Page{
|
|
Page: page.Page,
|
|
Count: page.Count,
|
|
Cursor: page.Cursor,
|
|
})
|
|
}
|
|
|
|
if pp.PageCursor == nil {
|
|
pp.PrevPage = nil
|
|
}
|
|
|
|
if pp.NextPage != nil && len(pp.PageNavigation) == 1 {
|
|
pp.NextPage = nil
|
|
}
|
|
|
|
if pp.IncTotal {
|
|
pp.Total = total
|
|
}
|
|
|
|
if !pp.IncPageNavigation {
|
|
pp.PageNavigation = nil
|
|
}
|
|
|
|
return
|
|
}
|