@@ -109,20 +109,34 @@ func (r *RESTClient) sendRequest(method string, url url.URL, body interface{}) (
109109 return r .client .Do (req )
110110}
111111
112- func parseResponse (reader io.Reader , data interface {}) error {
112+ func parseResponseEnvelope (reader io.Reader ) ( * response , error ) {
113113 // Schema for Tunnelstore responses in the v1 API.
114114 // Roughly, it's a wrapper around a particular result that adds failures/errors/etc
115115 var result response
116116 // First, parse the wrapper and check the API call succeeded
117117 if err := json .NewDecoder (reader ).Decode (& result ); err != nil {
118- return errors .Wrap (err , "failed to decode response" )
118+ return nil , errors .Wrap (err , "failed to decode response" )
119119 }
120120 if err := result .checkErrors (); err != nil {
121- return err
121+ return nil , err
122122 }
123123 if ! result .Success {
124- return ErrAPINoSuccess
124+ return nil , ErrAPINoSuccess
125125 }
126+
127+ return & result , nil
128+ }
129+
130+ func parseResponse (reader io.Reader , data interface {}) error {
131+ result , err := parseResponseEnvelope (reader )
132+ if err != nil {
133+ return err
134+ }
135+
136+ return parseResponseBody (result , data )
137+ }
138+
139+ func parseResponseBody (result * response , data interface {}) error {
126140 // At this point we know the API call succeeded, so, parse out the inner
127141 // result into the datatype provided as a parameter.
128142 if err := json .Unmarshal (result .Result , & data ); err != nil {
@@ -131,11 +145,58 @@ func parseResponse(reader io.Reader, data interface{}) error {
131145 return nil
132146}
133147
148+ func fetchExhaustively [T any ](requestFn func (int ) (* http.Response , error )) ([]* T , error ) {
149+ page := 0
150+ var fullResponse []* T
151+
152+ for {
153+ page += 1
154+ envelope , parsedBody , err := fetchPage [T ](requestFn , page )
155+
156+ if err != nil {
157+ return nil , errors .Wrap (err , fmt .Sprintf ("Error Parsing page %d" , page ))
158+ }
159+
160+ fullResponse = append (fullResponse , parsedBody ... )
161+ if envelope .Pagination .Count < envelope .Pagination .PerPage || len (fullResponse ) >= envelope .Pagination .TotalCount {
162+ break
163+ }
164+
165+ }
166+ return fullResponse , nil
167+ }
168+
169+ func fetchPage [T any ](requestFn func (int ) (* http.Response , error ), page int ) (* response , []* T , error ) {
170+ pageResp , err := requestFn (page )
171+ if err != nil {
172+ return nil , nil , errors .Wrap (err , "REST request failed" )
173+ }
174+ defer pageResp .Body .Close ()
175+ if pageResp .StatusCode == http .StatusOK {
176+ envelope , err := parseResponseEnvelope (pageResp .Body )
177+ if err != nil {
178+ return nil , nil , err
179+ }
180+ var parsedRspBody []* T
181+ return envelope , parsedRspBody , parseResponseBody (envelope , & parsedRspBody )
182+
183+ }
184+ return nil , nil , errors .New (fmt .Sprintf ("Failed to fetch page. Server returned: %d" , pageResp .StatusCode ))
185+ }
186+
134187type response struct {
135- Success bool `json:"success,omitempty"`
136- Errors []apiErr `json:"errors,omitempty"`
137- Messages []string `json:"messages,omitempty"`
138- Result json.RawMessage `json:"result,omitempty"`
188+ Success bool `json:"success,omitempty"`
189+ Errors []apiErr `json:"errors,omitempty"`
190+ Messages []string `json:"messages,omitempty"`
191+ Result json.RawMessage `json:"result,omitempty"`
192+ Pagination Pagination `json:"result_info,omitempty"`
193+ }
194+
195+ type Pagination struct {
196+ Count int `json:"count,omitempty"`
197+ Page int `json:"page,omitempty"`
198+ PerPage int `json:"per_page,omitempty"`
199+ TotalCount int `json:"total_count,omitempty"`
139200}
140201
141202func (r * response ) checkErrors () error {
0 commit comments