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