11package splunk
22
33import (
4- "bytes"
54 "context"
65 "crypto/tls"
76 "encoding/json"
@@ -202,17 +201,17 @@ type SplunkMessage struct {
202201}
203202
204203// JobStatus retrieves the current status of a search job.
205- func (c * Client ) JobStatus (sid string ) (bool , string , []SplunkMessage , error ) {
204+ func (c * Client ) JobStatus (sid string ) (bool , string , []SplunkMessage , int , error ) {
206205 endpoint , err := c .createAPIURL ("search" , "jobs" , sid )
207206 if err != nil {
208- return false , "" , nil , err
207+ return false , "" , nil , 0 , err
209208 }
210209 c .Log .Debugf (`Request: GET %s
211210` , endpoint )
212211
213212 req , err := http .NewRequest ("GET" , endpoint , nil )
214213 if err != nil {
215- return false , "" , nil , err
214+ return false , "" , nil , 0 , err
216215 }
217216
218217 q := req .URL .Query ()
@@ -221,12 +220,12 @@ func (c *Client) JobStatus(sid string) (bool, string, []SplunkMessage, error) {
221220
222221 resp , err := c .doRequest (req )
223222 if err != nil {
224- return false , "" , nil , err
223+ return false , "" , nil , 0 , err
225224 }
226225 defer resp .Body .Close ()
227226
228227 if err := c .handleFailedResponse (resp , http .StatusOK ); err != nil {
229- return false , "" , nil , err
228+ return false , "" , nil , 0 , err
230229 }
231230
232231 var status struct {
@@ -235,25 +234,27 @@ func (c *Client) JobStatus(sid string) (bool, string, []SplunkMessage, error) {
235234 IsDone bool `json:"isDone"`
236235 DispatchState string `json:"dispatchState"`
237236 Messages []SplunkMessage `json:"messages"`
237+ ResultCount int `json:"resultCount"`
238238 } `json:"content"`
239239 } `json:"entry"`
240240 }
241241 bodyBytes , err := io .ReadAll (resp .Body )
242242 if err != nil {
243- return false , "" , nil , fmt .Errorf (`failed to read job status response body: %w` , err )
243+ return false , "" , nil , 0 , fmt .Errorf (`failed to read job status response body: %w` , err )
244244 }
245245
246246 if err := json .Unmarshal (bodyBytes , & status ); err != nil {
247- return false , "" , nil , fmt .Errorf (`failed to decode job status JSON: %w. Received: %s` , err , string (bodyBytes ))
247+ return false , "" , nil , 0 , fmt .Errorf (`failed to decode job status JSON: %w. Received: %s` , err , string (bodyBytes ))
248248 }
249249
250250 if len (status .Entry ) == 0 {
251- return false , "" , nil , errors .New ("job status not found in response" )
251+ return false , "" , nil , 0 , errors .New ("job status not found in response" )
252252 }
253253 content := status .Entry [0 ].Content
254- return content .IsDone , content .DispatchState , content .Messages , nil
254+ return content .IsDone , content .DispatchState , content .Messages , content . ResultCount , nil
255255}
256256
257+
257258// WaitForJob waits for a job to finish, with a timeout.
258259func (c * Client ) WaitForJob (ctx context.Context , sid string ) error {
259260 c .Log .Println ("Waiting for job to complete..." )
@@ -265,7 +266,7 @@ func (c *Client) WaitForJob(ctx context.Context, sid string) error {
265266 case <- ctx .Done ():
266267 return ctx .Err ()
267268 case <- ticker .C :
268- done , jobState , messages , err := c .JobStatus (sid )
269+ done , jobState , messages , _ , err := c .JobStatus (sid )
269270 if err != nil {
270271 return err
271272 }
@@ -291,47 +292,84 @@ func (c *Client) WaitForJob(ctx context.Context, sid string) error {
291292 }
292293}
293294
294- // Results fetches the results of a completed search job.
295+ // Results fetches the results of a completed search job, handling pagination .
295296func (c * Client ) Results (sid string , limit int ) (string , error ) {
296- endpoint , err := c .createAPIURL ("search" , "jobs" , sid , "results" )
297+ // 1. Get the total number of results for the job
298+ _ , _ , _ , totalResults , err := c .JobStatus (sid )
297299 if err != nil {
298- return "" , err
300+ return "" , fmt . Errorf ( "could not get job status before fetching results: %w" , err )
299301 }
300- c .Log .Debugf (`Request: GET %s
301- ` ,
302- endpoint )
303302
304- req , err := http .NewRequest ("GET" , endpoint , nil )
305- if err != nil {
306- return "" , err
303+ // 2. Determine the number of results to fetch
304+ fetchCount := limit
305+ if limit == 0 || (limit > 0 && limit > totalResults ) {
306+ fetchCount = totalResults
307307 }
308- q := req .URL .Query ()
309- q .Add ("output_mode" , "json" )
310- q .Add ("count" , fmt .Sprintf ("%d" , limit ))
311- req .URL .RawQuery = q .Encode ()
312308
313- resp , err := c .doRequest (req )
314- if err != nil {
315- return "" , err
309+ // 3. Fetch results, with pagination if necessary
310+ const maxCount = 50000 // Max results per request
311+ var allResults []json.RawMessage
312+
313+ for offset := 0 ; offset < fetchCount ; offset += maxCount {
314+ // Determine count for this specific request
315+ count := maxCount
316+ if offset + count > fetchCount {
317+ count = fetchCount - offset
318+ }
319+
320+ // Prepare request
321+ endpoint , err := c .createAPIURL ("search" , "jobs" , sid , "results" )
322+ if err != nil {
323+ return "" , err
324+ }
325+ c .Log .Debugf (`Request: GET %s (offset: %d, count: %d)
326+ ` , endpoint , offset , count )
327+
328+ req , err := http .NewRequest ("GET" , endpoint , nil )
329+ if err != nil {
330+ return "" , err
331+ }
332+ q := req .URL .Query ()
333+ q .Add ("output_mode" , "json" )
334+ q .Add ("offset" , fmt .Sprintf ("%d" , offset ))
335+ q .Add ("count" , fmt .Sprintf ("%d" , count ))
336+ req .URL .RawQuery = q .Encode ()
337+
338+ // Execute request
339+ resp , err := c .doRequest (req )
340+ if err != nil {
341+ return "" , err
342+ }
343+ defer resp .Body .Close ()
344+
345+ if err := c .handleFailedResponse (resp , http .StatusOK ); err != nil {
346+ return "" , err
347+ }
348+
349+ // Decode and append results
350+ var page struct {
351+ Results []json.RawMessage `json:"results"`
352+ }
353+ if err := json .NewDecoder (resp .Body ).Decode (& page ); err != nil {
354+ return "" , fmt .Errorf ("failed to decode results page: %w" , err )
355+ }
356+ allResults = append (allResults , page .Results ... )
316357 }
317- defer resp .Body .Close ()
318358
319- if err := c .handleFailedResponse (resp , http .StatusOK ); err != nil {
320- return "" , err
359+ // 4. Combine and format the final JSON output
360+ finalJSON := map [string ][]json.RawMessage {
361+ "results" : allResults ,
321362 }
322363
323- body , err := io . ReadAll ( resp . Body )
364+ prettyJSON , err := json . MarshalIndent ( finalJSON , "" , " " )
324365 if err != nil {
325- return "" , err
366+ return "" , fmt . Errorf ( "failed to marshal final results: %w" , err )
326367 }
327368
328- var prettyJSON bytes.Buffer
329- if err := json .Indent (& prettyJSON , body , "" , " " ); err != nil {
330- return string (body ), nil
331- }
332- return prettyJSON .String (), nil
369+ return string (prettyJSON ), nil
333370}
334371
372+
335373// CancelSearch sends a request to cancel a running job.
336374func (c * Client ) CancelSearch (sid string ) error {
337375 c .Log .Println (`
0 commit comments