3
0
Files
corteza/pkg/dal/iterator.go
Vivek Patel f3ad5e2a1a Fix divide by zero panic
Add check for zero before dividing it.
2022-09-30 19:48:00 +05:30

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 pp.Limit > 0 && 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
}