github.com/opentelekomcloud/gophertelekomcloud@v0.9.3/pagination/linked.go (about)

     1  package pagination
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"reflect"
     7  
     8  	"github.com/opentelekomcloud/gophertelekomcloud"
     9  	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
    10  )
    11  
    12  // LinkedPageBase may be embedded to implement a page that provides navigational "Next" and "Previous" links within its result.
    13  type LinkedPageBase struct {
    14  	PageResult
    15  
    16  	// LinkPath lists the keys that should be traversed within a response to arrive at the "next" pointer.
    17  	// If any link along the path is missing, an empty URL will be returned.
    18  	// If any link results in an unexpected value type, an error will be returned.
    19  	// When left as "nil", []string{"links", "next"} will be used as a default.
    20  	LinkPath []string
    21  }
    22  
    23  // NextPageURL extracts the pagination structure from a JSON response and returns the "next" link, if one is present.
    24  // It assumes that the links are available in a "links" element of the top-level response object.
    25  // If this is not the case, override NextPageURL on your result type.
    26  func (current LinkedPageBase) NextPageURL() (string, error) {
    27  	var path []string
    28  	var key string
    29  
    30  	if current.LinkPath == nil {
    31  		path = []string{"links", "next"}
    32  	} else {
    33  		path = current.LinkPath
    34  	}
    35  
    36  	submap := make(map[string]any)
    37  
    38  	err := extract.Into(bytes.NewReader(current.Body), &submap)
    39  	if err != nil {
    40  		err := golangsdk.ErrUnexpectedType{}
    41  		err.Expected = "map[string]any"
    42  		err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body))
    43  		return "", err
    44  	}
    45  
    46  	for {
    47  		key, path = path[0], path[1:]
    48  
    49  		value, ok := submap[key]
    50  		if !ok {
    51  			return "", nil
    52  		}
    53  
    54  		if len(path) > 0 {
    55  			submap, ok = value.(map[string]any)
    56  			if !ok {
    57  				err := golangsdk.ErrUnexpectedType{}
    58  				err.Expected = "map[string]any"
    59  				err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value))
    60  				return "", err
    61  			}
    62  		} else {
    63  			if value == nil {
    64  				// Actual null element.
    65  				return "", nil
    66  			}
    67  
    68  			url, ok := value.(string)
    69  			if !ok {
    70  				err := golangsdk.ErrUnexpectedType{}
    71  				err.Expected = "string"
    72  				err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value))
    73  				return "", err
    74  			}
    75  
    76  			return url, nil
    77  		}
    78  	}
    79  }
    80  
    81  // IsEmpty satisfies the IsEmpty method of the Page interface
    82  func (current LinkedPageBase) IsEmpty() (bool, error) {
    83  	body, err := current.GetBodyAsSlice()
    84  	if err != nil {
    85  		return false, fmt.Errorf("error converting page body to slice: %w", err)
    86  	}
    87  
    88  	return len(body) == 0, nil
    89  }
    90  
    91  // GetBody returns the linked page's body. This method is needed to satisfy the
    92  // Page interface.
    93  func (current LinkedPageBase) GetBody() []byte {
    94  	return current.Body
    95  }
    96  
    97  // WrapNextPageURL function use makerID to warp next page url,it returns the full url for request.
    98  func (current LinkedPageBase) WrapNextPageURL(markerID string) (string, error) {
    99  	limit := current.URL.Query().Get("limit")
   100  
   101  	if limit == "" {
   102  		return "", nil
   103  	}
   104  
   105  	q := current.URL.Query()
   106  
   107  	q.Set("marker", markerID)
   108  	current.URL.RawQuery = q.Encode()
   109  	return current.URL.String(), nil
   110  }