| package request |
| |
| import ( |
| "reflect" |
| "sync/atomic" |
| |
| "github.com/aws/aws-sdk-go/aws" |
| "github.com/aws/aws-sdk-go/aws/awsutil" |
| ) |
| |
| // A Pagination provides paginating of SDK API operations which are paginatable. |
| // Generally you should not use this type directly, but use the "Pages" API |
| // operations method to automatically perform pagination for you. Such as, |
| // "S3.ListObjectsPages", and "S3.ListObjectsPagesWithContext" methods. |
| // |
| // Pagination differs from a Paginator type in that pagination is the type that |
| // does the pagination between API operations, and Paginator defines the |
| // configuration that will be used per page request. |
| // |
| // cont := true |
| // for p.Next() && cont { |
| // data := p.Page().(*s3.ListObjectsOutput) |
| // // process the page's data |
| // } |
| // return p.Err() |
| // |
| // See service client API operation Pages methods for examples how the SDK will |
| // use the Pagination type. |
| type Pagination struct { |
| // Function to return a Request value for each pagination request. |
| // Any configuration or handlers that need to be applied to the request |
| // prior to getting the next page should be done here before the request |
| // returned. |
| // |
| // NewRequest should always be built from the same API operations. It is |
| // undefined if different API operations are returned on subsequent calls. |
| NewRequest func() (*Request, error) |
| |
| started bool |
| nextTokens []interface{} |
| |
| err error |
| curPage interface{} |
| } |
| |
| // HasNextPage will return true if Pagination is able to determine that the API |
| // operation has additional pages. False will be returned if there are no more |
| // pages remaining. |
| // |
| // Will always return true if Next has not been called yet. |
| func (p *Pagination) HasNextPage() bool { |
| return !(p.started && len(p.nextTokens) == 0) |
| } |
| |
| // Err returns the error Pagination encountered when retrieving the next page. |
| func (p *Pagination) Err() error { |
| return p.err |
| } |
| |
| // Page returns the current page. Page should only be called after a successful |
| // call to Next. It is undefined what Page will return if Page is called after |
| // Next returns false. |
| func (p *Pagination) Page() interface{} { |
| return p.curPage |
| } |
| |
| // Next will attempt to retrieve the next page for the API operation. When a page |
| // is retrieved true will be returned. If the page cannot be retrieved, or there |
| // are no more pages false will be returned. |
| // |
| // Use the Page method to retrieve the current page data. The data will need |
| // to be cast to the API operation's output type. |
| // |
| // Use the Err method to determine if an error occurred if Page returns false. |
| func (p *Pagination) Next() bool { |
| if !p.HasNextPage() { |
| return false |
| } |
| |
| req, err := p.NewRequest() |
| if err != nil { |
| p.err = err |
| return false |
| } |
| |
| if p.started { |
| for i, intok := range req.Operation.InputTokens { |
| awsutil.SetValueAtPath(req.Params, intok, p.nextTokens[i]) |
| } |
| } |
| p.started = true |
| |
| err = req.Send() |
| if err != nil { |
| p.err = err |
| return false |
| } |
| |
| p.nextTokens = req.nextPageTokens() |
| p.curPage = req.Data |
| |
| return true |
| } |
| |
| // A Paginator is the configuration data that defines how an API operation |
| // should be paginated. This type is used by the API service models to define |
| // the generated pagination config for service APIs. |
| // |
| // The Pagination type is what provides iterating between pages of an API. It |
| // is only used to store the token metadata the SDK should use for performing |
| // pagination. |
| type Paginator struct { |
| InputTokens []string |
| OutputTokens []string |
| LimitToken string |
| TruncationToken string |
| } |
| |
| // nextPageTokens returns the tokens to use when asking for the next page of data. |
| func (r *Request) nextPageTokens() []interface{} { |
| if r.Operation.Paginator == nil { |
| return nil |
| } |
| if r.Operation.TruncationToken != "" { |
| tr, _ := awsutil.ValuesAtPath(r.Data, r.Operation.TruncationToken) |
| if len(tr) == 0 { |
| return nil |
| } |
| |
| switch v := tr[0].(type) { |
| case *bool: |
| if !aws.BoolValue(v) { |
| return nil |
| } |
| case bool: |
| if v == false { |
| return nil |
| } |
| } |
| } |
| |
| tokens := []interface{}{} |
| tokenAdded := false |
| for _, outToken := range r.Operation.OutputTokens { |
| v, _ := awsutil.ValuesAtPath(r.Data, outToken) |
| if len(v) > 0 { |
| tokens = append(tokens, v[0]) |
| tokenAdded = true |
| } else { |
| tokens = append(tokens, nil) |
| } |
| } |
| if !tokenAdded { |
| return nil |
| } |
| |
| return tokens |
| } |
| |
| // Ensure a deprecated item is only logged once instead of each time its used. |
| func logDeprecatedf(logger aws.Logger, flag *int32, msg string) { |
| if logger == nil { |
| return |
| } |
| if atomic.CompareAndSwapInt32(flag, 0, 1) { |
| logger.Log(msg) |
| } |
| } |
| |
| var ( |
| logDeprecatedHasNextPage int32 |
| logDeprecatedNextPage int32 |
| logDeprecatedEachPage int32 |
| ) |
| |
| // HasNextPage returns true if this request has more pages of data available. |
| // |
| // Deprecated Use Pagination type for configurable pagination of API operations |
| func (r *Request) HasNextPage() bool { |
| logDeprecatedf(r.Config.Logger, &logDeprecatedHasNextPage, |
| "Request.HasNextPage deprecated. Use Pagination type for configurable pagination of API operations") |
| |
| return len(r.nextPageTokens()) > 0 |
| } |
| |
| // NextPage returns a new Request that can be executed to return the next |
| // page of result data. Call .Send() on this request to execute it. |
| // |
| // Deprecated Use Pagination type for configurable pagination of API operations |
| func (r *Request) NextPage() *Request { |
| logDeprecatedf(r.Config.Logger, &logDeprecatedNextPage, |
| "Request.NextPage deprecated. Use Pagination type for configurable pagination of API operations") |
| |
| tokens := r.nextPageTokens() |
| if len(tokens) == 0 { |
| return nil |
| } |
| |
| data := reflect.New(reflect.TypeOf(r.Data).Elem()).Interface() |
| nr := New(r.Config, r.ClientInfo, r.Handlers, r.Retryer, r.Operation, awsutil.CopyOf(r.Params), data) |
| for i, intok := range nr.Operation.InputTokens { |
| awsutil.SetValueAtPath(nr.Params, intok, tokens[i]) |
| } |
| return nr |
| } |
| |
| // EachPage iterates over each page of a paginated request object. The fn |
| // parameter should be a function with the following sample signature: |
| // |
| // func(page *T, lastPage bool) bool { |
| // return true // return false to stop iterating |
| // } |
| // |
| // Where "T" is the structure type matching the output structure of the given |
| // operation. For example, a request object generated by |
| // DynamoDB.ListTablesRequest() would expect to see dynamodb.ListTablesOutput |
| // as the structure "T". The lastPage value represents whether the page is |
| // the last page of data or not. The return value of this function should |
| // return true to keep iterating or false to stop. |
| // |
| // Deprecated Use Pagination type for configurable pagination of API operations |
| func (r *Request) EachPage(fn func(data interface{}, isLastPage bool) (shouldContinue bool)) error { |
| logDeprecatedf(r.Config.Logger, &logDeprecatedEachPage, |
| "Request.EachPage deprecated. Use Pagination type for configurable pagination of API operations") |
| |
| for page := r; page != nil; page = page.NextPage() { |
| if err := page.Send(); err != nil { |
| return err |
| } |
| if getNextPage := fn(page.Data, !page.HasNextPage()); !getNextPage { |
| return page.Error |
| } |
| } |
| |
| return nil |
| } |