way easier way to handle pagination
This commit is contained in:
parent
c2f0f06b59
commit
d47c0c8603
139
client.go
139
client.go
@ -3,13 +3,8 @@
|
||||
package netboxapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
e "git.stinnesbeck.com/nils/errorhandler"
|
||||
transport "github.com/go-openapi/runtime/client"
|
||||
"github.com/netbox-community/go-netbox/v3/netbox/client"
|
||||
)
|
||||
@ -21,13 +16,6 @@ type NetBoxAPI struct {
|
||||
URL string
|
||||
}
|
||||
|
||||
type apiResponse struct {
|
||||
Count int64 `json:"count"`
|
||||
Next string `json:"next"`
|
||||
Previous string `json:"previous"`
|
||||
Results []any `json:"results"`
|
||||
}
|
||||
|
||||
// NewNetBoxClient returns a client to NetBox at the given url,
|
||||
// using the provided token
|
||||
func NewNetBoxClient(token string, url string) (NetBoxAPI, error) {
|
||||
@ -50,130 +38,3 @@ func NewNetBoxClient(token string, url string) (NetBoxAPI, error) {
|
||||
}
|
||||
return nb, nil
|
||||
}
|
||||
|
||||
func (nb NetBoxAPI) handleRawPagination(url string) (*apiResponse, error) {
|
||||
var response apiResponse
|
||||
b, err := nb.rawNetBoxAPICall(url, http.MethodGet)
|
||||
if err != nil {
|
||||
return nil, e.FormatError("can't perform raw NetBox API call", err)
|
||||
}
|
||||
|
||||
// Unmarshal json into response
|
||||
if err := json.Unmarshal(b, &response); err != nil {
|
||||
return nil, e.FormatError("can't unmarshal json payload in raw NetBox API call", err)
|
||||
}
|
||||
|
||||
// there are no more pages left, return the response we got
|
||||
if response.Next == "" {
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// there are more pages left, get more responses from there
|
||||
|
||||
// recursive calling of handlePagination to get all following pages
|
||||
nextResponse, err := nb.handleRawPagination(response.Next)
|
||||
if err != nil {
|
||||
return nil, e.FormatError("can't get next response from api", err)
|
||||
}
|
||||
|
||||
// append all next responses' results to this response's result
|
||||
response.Results = append(response.Results, nextResponse.Results...)
|
||||
|
||||
// go back up the chain through recursion
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func (nb NetBoxAPI) handlePagination(url string, payload any) error {
|
||||
response, err := nb.handleRawPagination(url)
|
||||
if err != nil {
|
||||
return e.FormatError("can't handle pagination for url "+url, err)
|
||||
}
|
||||
|
||||
fmt.Println(response.Count)
|
||||
|
||||
// cast response into our payload
|
||||
if err := resultToOtherStructure(*response, &payload); err != nil {
|
||||
return e.FormatError("can't parse result to other Structure", err)
|
||||
}
|
||||
|
||||
// prettify.Print(payload)
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func resultToOtherStructure(input apiResponse, output any) error {
|
||||
// get JSON representation of results
|
||||
c, err := json.Marshal(input.Results)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Unmarshal onto specific structure via output
|
||||
return json.Unmarshal(c, &output)
|
||||
}
|
||||
|
||||
func (nb NetBoxAPI) rawNetBoxAPICall(url string, method string, data ...any) ([]byte, error) {
|
||||
// construct complete URL (url already comes with a "/" after the "/api" part)
|
||||
// url = fmt.Sprintf("https://%s/api%s", nb.URL, url)
|
||||
|
||||
// log.Printf("calling API on url: %s with method %s and data %+v\n", url, method, data)
|
||||
// create new transport client
|
||||
|
||||
c := &http.Client{}
|
||||
var req *http.Request
|
||||
|
||||
switch method {
|
||||
case http.MethodPatch, http.MethodPost, http.MethodPut:
|
||||
// to patch something we need data, check it exists
|
||||
if data == nil {
|
||||
return nil, fmt.Errorf("no data was provided, please provide data to use PATCH")
|
||||
}
|
||||
|
||||
payload, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, e.FormatError(fmt.Sprintf("can't marshal data %+v for apicall %s", data, url), err)
|
||||
}
|
||||
|
||||
// construct request
|
||||
innerRequest, err := http.NewRequest(method, url, bytes.NewBuffer(payload))
|
||||
if err != nil {
|
||||
return nil, e.FormatError(fmt.Sprintf("can't construct new request (method: %s, URL: %s, payload %s)", method, url, payload), err)
|
||||
}
|
||||
|
||||
// hand over request to outer request
|
||||
req = innerRequest
|
||||
|
||||
case http.MethodGet:
|
||||
innerRequest, err := http.NewRequest(method, url, nil)
|
||||
if err != nil {
|
||||
return nil, e.FormatError(fmt.Sprintf("can't construct new request (method: %s, URL: %s)", method, url), err)
|
||||
}
|
||||
|
||||
// hand over request to outer request
|
||||
req = innerRequest
|
||||
}
|
||||
|
||||
// set request headers
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Token %s", nb.Token))
|
||||
|
||||
// call API client with request
|
||||
resp, err := c.Do(req)
|
||||
if err != nil {
|
||||
return nil, e.FormatError("can't get response from "+url, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// read body of response
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, e.FormatError("can't read response body", err)
|
||||
}
|
||||
|
||||
switch resp.StatusCode {
|
||||
case 200, 201:
|
||||
return body, nil
|
||||
default:
|
||||
return body, fmt.Errorf("got bad StatusCode %d in response from API, data is:\n\t-> %+v", resp.StatusCode, data)
|
||||
}
|
||||
}
|
||||
|
68
ipam.go
68
ipam.go
@ -34,32 +34,31 @@ func (nb NetBoxAPI) GetPrefixes(param ipam.IpamPrefixesListParams) ([]*models.Pr
|
||||
return nil, e.FormatError("can't get prefixes", err)
|
||||
}
|
||||
|
||||
// variable to hold all prefixes to return
|
||||
var prefixesToReturn []*models.Prefix
|
||||
|
||||
// append first page of models
|
||||
prefixesToReturn = append(prefixesToReturn, prefixes.Payload.Results...)
|
||||
|
||||
// check if there are more prefixes on other pages
|
||||
if prefixes.Payload.Next != nil {
|
||||
// there are more pages to get
|
||||
|
||||
// calculate new offset
|
||||
newOffset := *param.Offset + *param.Limit
|
||||
|
||||
// set new offset
|
||||
param.Offset = &newOffset
|
||||
|
||||
// get prefixes from next page
|
||||
nextPagePrefixes, err := nb.GetPrefixes(param)
|
||||
if err != nil {
|
||||
return nil, e.FormatError("can't get prefixes", err)
|
||||
}
|
||||
// append the results from next page to our prefixes to return
|
||||
prefixesToReturn = append(prefixesToReturn, nextPagePrefixes...)
|
||||
if prefixes.Payload.Next == nil {
|
||||
// return the results of the first batch, there are no others
|
||||
return prefixes.Payload.Results, nil
|
||||
}
|
||||
|
||||
return prefixesToReturn, nil
|
||||
// there are more pages to get
|
||||
|
||||
// calculate new offset
|
||||
newOffset := *param.Offset + *param.Limit
|
||||
|
||||
// set new offset
|
||||
param.Offset = &newOffset
|
||||
|
||||
// get prefixes from next page
|
||||
nextPagePrefixes, err := nb.GetPrefixes(param)
|
||||
if err != nil {
|
||||
return nil, e.FormatError("can't get prefixes", err)
|
||||
}
|
||||
|
||||
// append the results from next page to our prefixes to return
|
||||
prefixes.Payload.Results = append(prefixes.Payload.Results, nextPagePrefixes...)
|
||||
|
||||
// return all prefixes
|
||||
return prefixes.Payload.Results, nil
|
||||
}
|
||||
|
||||
// GetIPAddresses returns IP addresses from the api filtered by the parameter list param
|
||||
@ -90,20 +89,27 @@ func (nb NetBoxAPI) GetIPAddresses(param ipam.IpamIPAddressesListParams) ([]*mod
|
||||
|
||||
// check if there are no more prefixes on other pages
|
||||
if addresses.Payload.Next == nil {
|
||||
// return the results of the first batch, there are no others!
|
||||
// return the results of the first batch, there are no others
|
||||
return addresses.Payload.Results, nil
|
||||
}
|
||||
|
||||
// variable to hold all prefixes to return
|
||||
var addressesToReturn []*models.IPAddress
|
||||
// there are more pages to get
|
||||
|
||||
if err := nb.handlePagination(addresses.Payload.Next.String(), &addressesToReturn); err != nil {
|
||||
return nil, e.FormatError("can't handle Pagination", err)
|
||||
// calculate new offset
|
||||
newOffset := *param.Offset + *param.Limit
|
||||
|
||||
// set new offset
|
||||
param.Offset = &newOffset
|
||||
|
||||
// get prefixes from next page
|
||||
nextPageIPAddresses, err := nb.GetIPAddresses(param)
|
||||
if err != nil {
|
||||
return nil, e.FormatError("can't get IP addresses", err)
|
||||
}
|
||||
|
||||
// append append first batch to the other batches
|
||||
addressesToReturn = append(addressesToReturn, addresses.Payload.Results...)
|
||||
// append the results from next page to our prefixes to return
|
||||
addresses.Payload.Results = append(addresses.Payload.Results, nextPageIPAddresses...)
|
||||
|
||||
// return all addresses
|
||||
return addressesToReturn, nil
|
||||
return addresses.Payload.Results, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user