1package linodego 2 3/** 4 * Pagination and Filtering types and helpers 5 */ 6 7import ( 8 "context" 9 "fmt" 10 "log" 11 "strconv" 12 13 "gopkg.in/resty.v1" 14) 15 16// PageOptions are the pagination parameters for List endpoints 17type PageOptions struct { 18 Page int `url:"page,omitempty" json:"page"` 19 Pages int `url:"pages,omitempty" json:"pages"` 20 Results int `url:"results,omitempty" json:"results"` 21} 22 23// ListOptions are the pagination and filtering (TODO) parameters for endpoints 24type ListOptions struct { 25 *PageOptions 26 Filter string 27} 28 29// NewListOptions simplified construction of ListOptions using only 30// the two writable properties, Page and Filter 31func NewListOptions(Page int, Filter string) *ListOptions { 32 return &ListOptions{PageOptions: &PageOptions{Page: Page}, Filter: Filter} 33 34} 35 36// listHelper abstracts fetching and pagination for GET endpoints that 37// do not require any Ids (top level endpoints). 38// When opts (or opts.Page) is nil, all pages will be fetched and 39// returned in a single (endpoint-specific)PagedResponse 40// opts.results and opts.pages will be updated from the API response 41func (c *Client) listHelper(ctx context.Context, i interface{}, opts *ListOptions) error { 42 req := c.R(ctx) 43 if opts != nil && opts.PageOptions != nil && opts.Page > 0 { 44 req.SetQueryParam("page", strconv.Itoa(opts.Page)) 45 } 46 47 var ( 48 err error 49 pages int 50 results int 51 r *resty.Response 52 ) 53 54 if opts != nil && len(opts.Filter) > 0 { 55 req.SetHeader("X-Filter", opts.Filter) 56 } 57 58 switch v := i.(type) { 59 case *LinodeKernelsPagedResponse: 60 if r, err = coupleAPIErrors(req.SetResult(LinodeKernelsPagedResponse{}).Get(v.endpoint(c))); err == nil { 61 pages = r.Result().(*LinodeKernelsPagedResponse).Pages 62 results = r.Result().(*LinodeKernelsPagedResponse).Results 63 v.appendData(r.Result().(*LinodeKernelsPagedResponse)) 64 } 65 case *LinodeTypesPagedResponse: 66 if r, err = coupleAPIErrors(req.SetResult(LinodeTypesPagedResponse{}).Get(v.endpoint(c))); err == nil { 67 pages = r.Result().(*LinodeTypesPagedResponse).Pages 68 results = r.Result().(*LinodeTypesPagedResponse).Results 69 v.appendData(r.Result().(*LinodeTypesPagedResponse)) 70 } 71 case *ImagesPagedResponse: 72 if r, err = coupleAPIErrors(req.SetResult(ImagesPagedResponse{}).Get(v.endpoint(c))); err == nil { 73 pages = r.Result().(*ImagesPagedResponse).Pages 74 results = r.Result().(*ImagesPagedResponse).Results 75 v.appendData(r.Result().(*ImagesPagedResponse)) 76 } 77 case *StackscriptsPagedResponse: 78 if r, err = coupleAPIErrors(req.SetResult(StackscriptsPagedResponse{}).Get(v.endpoint(c))); err == nil { 79 pages = r.Result().(*StackscriptsPagedResponse).Pages 80 results = r.Result().(*StackscriptsPagedResponse).Results 81 v.appendData(r.Result().(*StackscriptsPagedResponse)) 82 } 83 case *InstancesPagedResponse: 84 if r, err = coupleAPIErrors(req.SetResult(InstancesPagedResponse{}).Get(v.endpoint(c))); err == nil { 85 pages = r.Result().(*InstancesPagedResponse).Pages 86 results = r.Result().(*InstancesPagedResponse).Results 87 v.appendData(r.Result().(*InstancesPagedResponse)) 88 } 89 case *RegionsPagedResponse: 90 if r, err = coupleAPIErrors(req.SetResult(RegionsPagedResponse{}).Get(v.endpoint(c))); err == nil { 91 pages = r.Result().(*RegionsPagedResponse).Pages 92 results = r.Result().(*RegionsPagedResponse).Results 93 v.appendData(r.Result().(*RegionsPagedResponse)) 94 } 95 case *VolumesPagedResponse: 96 if r, err = coupleAPIErrors(req.SetResult(VolumesPagedResponse{}).Get(v.endpoint(c))); err == nil { 97 pages = r.Result().(*VolumesPagedResponse).Pages 98 results = r.Result().(*VolumesPagedResponse).Results 99 v.appendData(r.Result().(*VolumesPagedResponse)) 100 } 101 case *DomainsPagedResponse: 102 if r, err = coupleAPIErrors(req.SetResult(DomainsPagedResponse{}).Get(v.endpoint(c))); err == nil { 103 response, ok := r.Result().(*DomainsPagedResponse) 104 if !ok { 105 return fmt.Errorf("Response is not a *DomainsPagedResponse") 106 } 107 pages = response.Pages 108 results = response.Results 109 v.appendData(response) 110 } 111 case *EventsPagedResponse: 112 if r, err = coupleAPIErrors(req.SetResult(EventsPagedResponse{}).Get(v.endpoint(c))); err == nil { 113 pages = r.Result().(*EventsPagedResponse).Pages 114 results = r.Result().(*EventsPagedResponse).Results 115 v.appendData(r.Result().(*EventsPagedResponse)) 116 } 117 case *LongviewSubscriptionsPagedResponse: 118 if r, err = coupleAPIErrors(req.SetResult(LongviewSubscriptionsPagedResponse{}).Get(v.endpoint(c))); err == nil { 119 pages = r.Result().(*LongviewSubscriptionsPagedResponse).Pages 120 results = r.Result().(*LongviewSubscriptionsPagedResponse).Results 121 v.appendData(r.Result().(*LongviewSubscriptionsPagedResponse)) 122 } 123 case *LongviewClientsPagedResponse: 124 if r, err = coupleAPIErrors(req.SetResult(LongviewClientsPagedResponse{}).Get(v.endpoint(c))); err == nil { 125 pages = r.Result().(*LongviewClientsPagedResponse).Pages 126 results = r.Result().(*LongviewClientsPagedResponse).Results 127 v.appendData(r.Result().(*LongviewClientsPagedResponse)) 128 } 129 case *IPAddressesPagedResponse: 130 if r, err = coupleAPIErrors(req.SetResult(IPAddressesPagedResponse{}).Get(v.endpoint(c))); err == nil { 131 pages = r.Result().(*IPAddressesPagedResponse).Pages 132 results = r.Result().(*IPAddressesPagedResponse).Results 133 v.appendData(r.Result().(*IPAddressesPagedResponse)) 134 } 135 case *IPv6PoolsPagedResponse: 136 if r, err = coupleAPIErrors(req.SetResult(IPv6PoolsPagedResponse{}).Get(v.endpoint(c))); err == nil { 137 pages = r.Result().(*IPv6PoolsPagedResponse).Pages 138 results = r.Result().(*IPv6PoolsPagedResponse).Results 139 v.appendData(r.Result().(*IPv6PoolsPagedResponse)) 140 } 141 case *IPv6RangesPagedResponse: 142 if r, err = coupleAPIErrors(req.SetResult(IPv6RangesPagedResponse{}).Get(v.endpoint(c))); err == nil { 143 pages = r.Result().(*IPv6RangesPagedResponse).Pages 144 results = r.Result().(*IPv6RangesPagedResponse).Results 145 v.appendData(r.Result().(*IPv6RangesPagedResponse)) 146 // @TODO consolidate this type with IPv6PoolsPagedResponse? 147 } 148 case *SSHKeysPagedResponse: 149 if r, err = coupleAPIErrors(req.SetResult(SSHKeysPagedResponse{}).Get(v.endpoint(c))); err == nil { 150 response, ok := r.Result().(*SSHKeysPagedResponse) 151 if !ok { 152 return fmt.Errorf("Response is not a *SSHKeysPagedResponse") 153 } 154 pages = response.Pages 155 results = response.Results 156 v.appendData(response) 157 } 158 case *TicketsPagedResponse: 159 if r, err = coupleAPIErrors(req.SetResult(TicketsPagedResponse{}).Get(v.endpoint(c))); err == nil { 160 pages = r.Result().(*TicketsPagedResponse).Pages 161 results = r.Result().(*TicketsPagedResponse).Results 162 v.appendData(r.Result().(*TicketsPagedResponse)) 163 } 164 case *InvoicesPagedResponse: 165 if r, err = coupleAPIErrors(req.SetResult(InvoicesPagedResponse{}).Get(v.endpoint(c))); err == nil { 166 pages = r.Result().(*InvoicesPagedResponse).Pages 167 results = r.Result().(*InvoicesPagedResponse).Results 168 v.appendData(r.Result().(*InvoicesPagedResponse)) 169 } 170 case *NotificationsPagedResponse: 171 if r, err = coupleAPIErrors(req.SetResult(NotificationsPagedResponse{}).Get(v.endpoint(c))); err == nil { 172 pages = r.Result().(*NotificationsPagedResponse).Pages 173 results = r.Result().(*NotificationsPagedResponse).Results 174 v.appendData(r.Result().(*NotificationsPagedResponse)) 175 } 176 case *NodeBalancersPagedResponse: 177 if r, err = coupleAPIErrors(req.SetResult(NodeBalancersPagedResponse{}).Get(v.endpoint(c))); err == nil { 178 pages = r.Result().(*NodeBalancersPagedResponse).Pages 179 results = r.Result().(*NodeBalancersPagedResponse).Results 180 v.appendData(r.Result().(*NodeBalancersPagedResponse)) 181 } 182 case *TagsPagedResponse: 183 if r, err = coupleAPIErrors(req.SetResult(TagsPagedResponse{}).Get(v.endpoint(c))); err == nil { 184 pages = r.Result().(*TagsPagedResponse).Pages 185 results = r.Result().(*TagsPagedResponse).Results 186 v.appendData(r.Result().(*TagsPagedResponse)) 187 } 188 case *TokensPagedResponse: 189 if r, err = coupleAPIErrors(req.SetResult(TokensPagedResponse{}).Get(v.endpoint(c))); err == nil { 190 pages = r.Result().(*TokensPagedResponse).Pages 191 results = r.Result().(*TokensPagedResponse).Results 192 v.appendData(r.Result().(*TokensPagedResponse)) 193 } 194 case *UsersPagedResponse: 195 if r, err = coupleAPIErrors(req.SetResult(UsersPagedResponse{}).Get(v.endpoint(c))); err == nil { 196 pages = r.Result().(*UsersPagedResponse).Pages 197 results = r.Result().(*UsersPagedResponse).Results 198 v.appendData(r.Result().(*UsersPagedResponse)) 199 } 200 /** 201 case AccountOauthClientsPagedResponse: 202 case AccountPaymentsPagedResponse: 203 case ProfileAppsPagedResponse: 204 case ProfileWhitelistPagedResponse: 205 case ManagedContactsPagedResponse: 206 case ManagedCredentialsPagedResponse: 207 case ManagedIssuesPagedResponse: 208 case ManagedLinodeSettingsPagedResponse: 209 case ManagedServicesPagedResponse: 210 **/ 211 default: 212 log.Fatalf("listHelper interface{} %+v used", i) 213 } 214 215 if err != nil { 216 return err 217 } 218 219 if opts == nil { 220 for page := 2; page <= pages; page = page + 1 { 221 if err := c.listHelper(ctx, i, &ListOptions{PageOptions: &PageOptions{Page: page}}); err != nil { 222 return err 223 } 224 } 225 } else { 226 if opts.PageOptions == nil { 227 opts.PageOptions = &PageOptions{} 228 } 229 230 if opts.Page == 0 { 231 for page := 2; page <= pages; page = page + 1 { 232 opts.Page = page 233 if err := c.listHelper(ctx, i, opts); err != nil { 234 return err 235 } 236 } 237 } 238 opts.Results = results 239 opts.Pages = pages 240 } 241 242 return nil 243} 244 245// listHelperWithID abstracts fetching and pagination for GET endpoints that 246// require an Id (second level endpoints). 247// When opts (or opts.Page) is nil, all pages will be fetched and 248// returned in a single (endpoint-specific)PagedResponse 249// opts.results and opts.pages will be updated from the API response 250func (c *Client) listHelperWithID(ctx context.Context, i interface{}, idRaw interface{}, opts *ListOptions) error { 251 req := c.R(ctx) 252 if opts != nil && opts.Page > 0 { 253 req.SetQueryParam("page", strconv.Itoa(opts.Page)) 254 } 255 256 var ( 257 err error 258 pages int 259 results int 260 r *resty.Response 261 ) 262 263 id, _ := idRaw.(int) 264 265 if opts != nil && len(opts.Filter) > 0 { 266 req.SetHeader("X-Filter", opts.Filter) 267 } 268 269 switch v := i.(type) { 270 case *InvoiceItemsPagedResponse: 271 if r, err = coupleAPIErrors(req.SetResult(InvoiceItemsPagedResponse{}).Get(v.endpointWithID(c, id))); err == nil { 272 pages = r.Result().(*InvoiceItemsPagedResponse).Pages 273 results = r.Result().(*InvoiceItemsPagedResponse).Results 274 v.appendData(r.Result().(*InvoiceItemsPagedResponse)) 275 } 276 case *DomainRecordsPagedResponse: 277 if r, err = coupleAPIErrors(req.SetResult(DomainRecordsPagedResponse{}).Get(v.endpointWithID(c, id))); err == nil { 278 response, ok := r.Result().(*DomainRecordsPagedResponse) 279 if !ok { 280 return fmt.Errorf("Response is not a *DomainRecordsPagedResponse") 281 } 282 pages = response.Pages 283 results = response.Results 284 v.appendData(response) 285 } 286 case *InstanceConfigsPagedResponse: 287 if r, err = coupleAPIErrors(req.SetResult(InstanceConfigsPagedResponse{}).Get(v.endpointWithID(c, id))); err == nil { 288 pages = r.Result().(*InstanceConfigsPagedResponse).Pages 289 results = r.Result().(*InstanceConfigsPagedResponse).Results 290 v.appendData(r.Result().(*InstanceConfigsPagedResponse)) 291 } 292 case *InstanceDisksPagedResponse: 293 if r, err = coupleAPIErrors(req.SetResult(InstanceDisksPagedResponse{}).Get(v.endpointWithID(c, id))); err == nil { 294 pages = r.Result().(*InstanceDisksPagedResponse).Pages 295 results = r.Result().(*InstanceDisksPagedResponse).Results 296 v.appendData(r.Result().(*InstanceDisksPagedResponse)) 297 } 298 case *NodeBalancerConfigsPagedResponse: 299 if r, err = coupleAPIErrors(req.SetResult(NodeBalancerConfigsPagedResponse{}).Get(v.endpointWithID(c, id))); err == nil { 300 pages = r.Result().(*NodeBalancerConfigsPagedResponse).Pages 301 results = r.Result().(*NodeBalancerConfigsPagedResponse).Results 302 v.appendData(r.Result().(*NodeBalancerConfigsPagedResponse)) 303 } 304 case *InstanceVolumesPagedResponse: 305 if r, err = coupleAPIErrors(req.SetResult(InstanceVolumesPagedResponse{}).Get(v.endpointWithID(c, id))); err == nil { 306 pages = r.Result().(*InstanceVolumesPagedResponse).Pages 307 results = r.Result().(*InstanceVolumesPagedResponse).Results 308 v.appendData(r.Result().(*InstanceVolumesPagedResponse)) 309 } 310 case *TaggedObjectsPagedResponse: 311 idStr := idRaw.(string) 312 313 if r, err = coupleAPIErrors(req.SetResult(TaggedObjectsPagedResponse{}).Get(v.endpointWithID(c, idStr))); err == nil { 314 pages = r.Result().(*TaggedObjectsPagedResponse).Pages 315 results = r.Result().(*TaggedObjectsPagedResponse).Results 316 v.appendData(r.Result().(*TaggedObjectsPagedResponse)) 317 } 318 /** 319 case TicketAttachmentsPagedResponse: 320 if r, err = req.SetResult(v).Get(v.endpoint(c)); r.Error() != nil { 321 return NewError(r) 322 } else if err == nil { 323 pages = r.Result().(*TicketAttachmentsPagedResponse).Pages 324 results = r.Result().(*TicketAttachmentsPagedResponse).Results 325 v.appendData(r.Result().(*TicketAttachmentsPagedResponse)) 326 } 327 case TicketRepliesPagedResponse: 328 if r, err = req.SetResult(v).Get(v.endpoint(c)); r.Error() != nil { 329 return NewError(r) 330 } else if err == nil { 331 pages = r.Result().(*TicketRepliesPagedResponse).Pages 332 results = r.Result().(*TicketRepliesPagedResponse).Results 333 v.appendData(r.Result().(*TicketRepliesPagedResponse)) 334 } 335 **/ 336 default: 337 log.Fatalf("Unknown listHelperWithID interface{} %T used", i) 338 } 339 340 if err != nil { 341 return err 342 } 343 344 if opts == nil { 345 for page := 2; page <= pages; page = page + 1 { 346 if err := c.listHelperWithID(ctx, i, id, &ListOptions{PageOptions: &PageOptions{Page: page}}); err != nil { 347 return err 348 } 349 } 350 } else { 351 if opts.PageOptions == nil { 352 opts.PageOptions = &PageOptions{} 353 } 354 if opts.Page == 0 { 355 for page := 2; page <= pages; page = page + 1 { 356 opts.Page = page 357 if err := c.listHelperWithID(ctx, i, id, opts); err != nil { 358 return err 359 } 360 } 361 } 362 opts.Results = results 363 opts.Pages = pages 364 } 365 366 return nil 367} 368 369// listHelperWithTwoIDs abstracts fetching and pagination for GET endpoints that 370// require twos IDs (third level endpoints). 371// When opts (or opts.Page) is nil, all pages will be fetched and 372// returned in a single (endpoint-specific)PagedResponse 373// opts.results and opts.pages will be updated from the API response 374func (c *Client) listHelperWithTwoIDs(ctx context.Context, i interface{}, firstID, secondID int, opts *ListOptions) error { 375 req := c.R(ctx) 376 if opts != nil && opts.Page > 0 { 377 req.SetQueryParam("page", strconv.Itoa(opts.Page)) 378 } 379 380 var ( 381 err error 382 pages int 383 results int 384 r *resty.Response 385 ) 386 387 if opts != nil && len(opts.Filter) > 0 { 388 req.SetHeader("X-Filter", opts.Filter) 389 } 390 391 switch v := i.(type) { 392 case *NodeBalancerNodesPagedResponse: 393 if r, err = coupleAPIErrors(req.SetResult(NodeBalancerNodesPagedResponse{}).Get(v.endpointWithTwoIDs(c, firstID, secondID))); err == nil { 394 pages = r.Result().(*NodeBalancerNodesPagedResponse).Pages 395 results = r.Result().(*NodeBalancerNodesPagedResponse).Results 396 v.appendData(r.Result().(*NodeBalancerNodesPagedResponse)) 397 } 398 399 default: 400 log.Fatalf("Unknown listHelperWithTwoIDs interface{} %T used", i) 401 } 402 403 if err != nil { 404 return err 405 } 406 407 if opts == nil { 408 for page := 2; page <= pages; page = page + 1 { 409 if err := c.listHelper(ctx, i, &ListOptions{PageOptions: &PageOptions{Page: page}}); err != nil { 410 return err 411 } 412 } 413 } else { 414 if opts.PageOptions == nil { 415 opts.PageOptions = &PageOptions{} 416 } 417 if opts.Page == 0 { 418 for page := 2; page <= pages; page = page + 1 { 419 opts.Page = page 420 if err := c.listHelperWithTwoIDs(ctx, i, firstID, secondID, opts); err != nil { 421 return err 422 } 423 } 424 } 425 opts.Results = results 426 opts.Pages = pages 427 } 428 429 return nil 430} 431