github.com/chnsz/golangsdk@v0.0.0-20240506093406-85a3fbfa605b/pagination/offset.go (about)

     1  package pagination
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  
     8  	"github.com/chnsz/golangsdk"
     9  )
    10  
    11  // OffsetPage is used for paging queries based on offset and limit.
    12  type OffsetPage interface {
    13  	Page
    14  
    15  	NextOffset() int
    16  }
    17  
    18  // OffsetPageBase is used for paging queries based on offset and limit.
    19  // DefaultOffset and DefaultLimit are used to calculate the next offset
    20  type OffsetPageBase struct {
    21  	PageResult
    22  
    23  	DefaultOffset int
    24  	DefaultLimit  int
    25  }
    26  
    27  // NextOffset returns offset of the next element of the page.
    28  func (current OffsetPageBase) NextOffset() int {
    29  	q := current.URL.Query()
    30  	offset, _ := strconv.Atoi(q.Get("offset"))
    31  	if offset == 0 {
    32  		offset = current.DefaultOffset
    33  	}
    34  
    35  	// get `limit` according to the following priority:
    36  	// query path > current page size > DefaultLimit
    37  	limit, _ := strconv.Atoi(q.Get("limit"))
    38  	if limit == 0 {
    39  		count, err := current.getPageSize()
    40  		if err != nil {
    41  			limit = current.DefaultLimit
    42  		}
    43  		limit = count
    44  		// save `limit` in query path
    45  		q.Set("limit", strconv.Itoa(limit))
    46  	}
    47  
    48  	return offset + limit
    49  }
    50  
    51  // NextPageURL generates the URL for the page of results after this one.
    52  func (current OffsetPageBase) NextPageURL() (string, error) {
    53  	next := current.NextOffset()
    54  	if next == 0 {
    55  		return "", nil
    56  	}
    57  
    58  	currentURL := current.URL
    59  	q := currentURL.Query()
    60  	q.Set("offset", strconv.Itoa(next))
    61  	currentURL.RawQuery = q.Encode()
    62  
    63  	return currentURL.String(), nil
    64  }
    65  
    66  // IsEmpty satisifies the IsEmpty method of the Page interface.
    67  func (current OffsetPageBase) IsEmpty() (bool, error) {
    68  	if pb, ok := current.Body.(map[string]interface{}); ok {
    69  		for k, v := range pb {
    70  			// ignore xxx_links
    71  			if !strings.HasSuffix(k, "links") {
    72  				// check the field's type. we only want []interface{} (which is really []map[string]interface{})
    73  				switch vt := v.(type) {
    74  				case []interface{}:
    75  					return len(vt) == 0, nil
    76  				}
    77  			}
    78  		}
    79  	}
    80  	if pb, ok := current.Body.([]interface{}); ok {
    81  		return len(pb) == 0, nil
    82  	}
    83  
    84  	err := golangsdk.ErrUnexpectedType{}
    85  	err.Expected = "map[string]interface{}/[]interface{}"
    86  	err.Actual = fmt.Sprintf("%T", current.Body)
    87  	return true, err
    88  }
    89  
    90  // GetBody returns the page's body. This method is needed to satisfy the Page interface.
    91  func (current OffsetPageBase) GetBody() interface{} {
    92  	return current.Body
    93  }
    94  
    95  // getPageSize calculates the count of items in an offset page
    96  func (current OffsetPageBase) getPageSize() (int, error) {
    97  	var pageSize int
    98  
    99  	switch pb := current.Body.(type) {
   100  	case map[string]interface{}:
   101  		for k, v := range pb {
   102  			// ignore xxx_links
   103  			if !strings.HasSuffix(k, "links") {
   104  				// check the field's type. we only want []interface{} (which is really []map[string]interface{})
   105  				switch vt := v.(type) {
   106  				case []interface{}:
   107  					pageSize = len(vt)
   108  					break
   109  				}
   110  			}
   111  		}
   112  	case []interface{}:
   113  		pageSize = len(pb)
   114  	default:
   115  		err := golangsdk.ErrUnexpectedType{}
   116  		err.Expected = "map[string]interface{}/[]interface{}"
   117  		err.Actual = fmt.Sprintf("%T", pb)
   118  		return 0, err
   119  	}
   120  
   121  	return pageSize, nil
   122  }