github.com/aavshr/aws-sdk-go@v1.41.3/aws/request/request_pagination.go (about) 1 package request 2 3 import ( 4 "reflect" 5 "sync/atomic" 6 7 "github.com/aavshr/aws-sdk-go/aws" 8 "github.com/aavshr/aws-sdk-go/aws/awsutil" 9 ) 10 11 // A Pagination provides paginating of SDK API operations which are paginatable. 12 // Generally you should not use this type directly, but use the "Pages" API 13 // operations method to automatically perform pagination for you. Such as, 14 // "S3.ListObjectsPages", and "S3.ListObjectsPagesWithContext" methods. 15 // 16 // Pagination differs from a Paginator type in that pagination is the type that 17 // does the pagination between API operations, and Paginator defines the 18 // configuration that will be used per page request. 19 // 20 // for p.Next() { 21 // data := p.Page().(*s3.ListObjectsOutput) 22 // // process the page's data 23 // // ... 24 // // break out of loop to stop fetching additional pages 25 // } 26 // 27 // return p.Err() 28 // 29 // See service client API operation Pages methods for examples how the SDK will 30 // use the Pagination type. 31 type Pagination struct { 32 // Function to return a Request value for each pagination request. 33 // Any configuration or handlers that need to be applied to the request 34 // prior to getting the next page should be done here before the request 35 // returned. 36 // 37 // NewRequest should always be built from the same API operations. It is 38 // undefined if different API operations are returned on subsequent calls. 39 NewRequest func() (*Request, error) 40 // EndPageOnSameToken, when enabled, will allow the paginator to stop on 41 // token that are the same as its previous tokens. 42 EndPageOnSameToken bool 43 44 started bool 45 prevTokens []interface{} 46 nextTokens []interface{} 47 48 err error 49 curPage interface{} 50 } 51 52 // HasNextPage will return true if Pagination is able to determine that the API 53 // operation has additional pages. False will be returned if there are no more 54 // pages remaining. 55 // 56 // Will always return true if Next has not been called yet. 57 func (p *Pagination) HasNextPage() bool { 58 if !p.started { 59 return true 60 } 61 62 hasNextPage := len(p.nextTokens) != 0 63 if p.EndPageOnSameToken { 64 return hasNextPage && !awsutil.DeepEqual(p.nextTokens, p.prevTokens) 65 } 66 return hasNextPage 67 } 68 69 // Err returns the error Pagination encountered when retrieving the next page. 70 func (p *Pagination) Err() error { 71 return p.err 72 } 73 74 // Page returns the current page. Page should only be called after a successful 75 // call to Next. It is undefined what Page will return if Page is called after 76 // Next returns false. 77 func (p *Pagination) Page() interface{} { 78 return p.curPage 79 } 80 81 // Next will attempt to retrieve the next page for the API operation. When a page 82 // is retrieved true will be returned. If the page cannot be retrieved, or there 83 // are no more pages false will be returned. 84 // 85 // Use the Page method to retrieve the current page data. The data will need 86 // to be cast to the API operation's output type. 87 // 88 // Use the Err method to determine if an error occurred if Page returns false. 89 func (p *Pagination) Next() bool { 90 if !p.HasNextPage() { 91 return false 92 } 93 94 req, err := p.NewRequest() 95 if err != nil { 96 p.err = err 97 return false 98 } 99 100 if p.started { 101 for i, intok := range req.Operation.InputTokens { 102 awsutil.SetValueAtPath(req.Params, intok, p.nextTokens[i]) 103 } 104 } 105 p.started = true 106 107 err = req.Send() 108 if err != nil { 109 p.err = err 110 return false 111 } 112 113 p.prevTokens = p.nextTokens 114 p.nextTokens = req.nextPageTokens() 115 p.curPage = req.Data 116 117 return true 118 } 119 120 // A Paginator is the configuration data that defines how an API operation 121 // should be paginated. This type is used by the API service models to define 122 // the generated pagination config for service APIs. 123 // 124 // The Pagination type is what provides iterating between pages of an API. It 125 // is only used to store the token metadata the SDK should use for performing 126 // pagination. 127 type Paginator struct { 128 InputTokens []string 129 OutputTokens []string 130 LimitToken string 131 TruncationToken string 132 } 133 134 // nextPageTokens returns the tokens to use when asking for the next page of data. 135 func (r *Request) nextPageTokens() []interface{} { 136 if r.Operation.Paginator == nil { 137 return nil 138 } 139 if r.Operation.TruncationToken != "" { 140 tr, _ := awsutil.ValuesAtPath(r.Data, r.Operation.TruncationToken) 141 if len(tr) == 0 { 142 return nil 143 } 144 145 switch v := tr[0].(type) { 146 case *bool: 147 if !aws.BoolValue(v) { 148 return nil 149 } 150 case bool: 151 if !v { 152 return nil 153 } 154 } 155 } 156 157 tokens := []interface{}{} 158 tokenAdded := false 159 for _, outToken := range r.Operation.OutputTokens { 160 vs, _ := awsutil.ValuesAtPath(r.Data, outToken) 161 if len(vs) == 0 { 162 tokens = append(tokens, nil) 163 continue 164 } 165 v := vs[0] 166 167 switch tv := v.(type) { 168 case *string: 169 if len(aws.StringValue(tv)) == 0 { 170 tokens = append(tokens, nil) 171 continue 172 } 173 case string: 174 if len(tv) == 0 { 175 tokens = append(tokens, nil) 176 continue 177 } 178 } 179 180 tokenAdded = true 181 tokens = append(tokens, v) 182 } 183 if !tokenAdded { 184 return nil 185 } 186 187 return tokens 188 } 189 190 // Ensure a deprecated item is only logged once instead of each time its used. 191 func logDeprecatedf(logger aws.Logger, flag *int32, msg string) { 192 if logger == nil { 193 return 194 } 195 if atomic.CompareAndSwapInt32(flag, 0, 1) { 196 logger.Log(msg) 197 } 198 } 199 200 var ( 201 logDeprecatedHasNextPage int32 202 logDeprecatedNextPage int32 203 logDeprecatedEachPage int32 204 ) 205 206 // HasNextPage returns true if this request has more pages of data available. 207 // 208 // Deprecated Use Pagination type for configurable pagination of API operations 209 func (r *Request) HasNextPage() bool { 210 logDeprecatedf(r.Config.Logger, &logDeprecatedHasNextPage, 211 "Request.HasNextPage deprecated. Use Pagination type for configurable pagination of API operations") 212 213 return len(r.nextPageTokens()) > 0 214 } 215 216 // NextPage returns a new Request that can be executed to return the next 217 // page of result data. Call .Send() on this request to execute it. 218 // 219 // Deprecated Use Pagination type for configurable pagination of API operations 220 func (r *Request) NextPage() *Request { 221 logDeprecatedf(r.Config.Logger, &logDeprecatedNextPage, 222 "Request.NextPage deprecated. Use Pagination type for configurable pagination of API operations") 223 224 tokens := r.nextPageTokens() 225 if len(tokens) == 0 { 226 return nil 227 } 228 229 data := reflect.New(reflect.TypeOf(r.Data).Elem()).Interface() 230 nr := New(r.Config, r.ClientInfo, r.Handlers, r.Retryer, r.Operation, awsutil.CopyOf(r.Params), data) 231 for i, intok := range nr.Operation.InputTokens { 232 awsutil.SetValueAtPath(nr.Params, intok, tokens[i]) 233 } 234 return nr 235 } 236 237 // EachPage iterates over each page of a paginated request object. The fn 238 // parameter should be a function with the following sample signature: 239 // 240 // func(page *T, lastPage bool) bool { 241 // return true // return false to stop iterating 242 // } 243 // 244 // Where "T" is the structure type matching the output structure of the given 245 // operation. For example, a request object generated by 246 // DynamoDB.ListTablesRequest() would expect to see dynamodb.ListTablesOutput 247 // as the structure "T". The lastPage value represents whether the page is 248 // the last page of data or not. The return value of this function should 249 // return true to keep iterating or false to stop. 250 // 251 // Deprecated Use Pagination type for configurable pagination of API operations 252 func (r *Request) EachPage(fn func(data interface{}, isLastPage bool) (shouldContinue bool)) error { 253 logDeprecatedf(r.Config.Logger, &logDeprecatedEachPage, 254 "Request.EachPage deprecated. Use Pagination type for configurable pagination of API operations") 255 256 for page := r; page != nil; page = page.NextPage() { 257 if err := page.Send(); err != nil { 258 return err 259 } 260 if getNextPage := fn(page.Data, !page.HasNextPage()); !getNextPage { 261 return page.Error 262 } 263 } 264 265 return nil 266 }