@@ -18,13 +18,16 @@ import (
1818)
1919
2020const (
21- WORKSPACE_URL = "https://api.getpostman.com/workspaces/%s"
22- ENVIRONMENTS_URL = "https://api.getpostman.com/environments/%s"
23- COLLECTIONS_URL = "https://api.getpostman.com/collections/%s"
24- userAgent = "PostmanRuntime/7.26.8"
25- defaultContentType = "*"
21+ WORKSPACE_URL = "https://api.getpostman.com/workspaces/%s"
22+ ENVIRONMENTS_URL = "https://api.getpostman.com/environments/%s"
23+ COLLECTIONS_URL = "https://api.getpostman.com/collections/%s"
24+ userAgent = "PostmanRuntime/7.26.8"
25+ defaultContentType = "*"
26+ abortScanAPIReqLimitThreshold = 0.8 // Abort scan if 80% of monthly api request limit is reached
2627)
2728
29+ var errAbortScanDueToAPIRateLimit = fmt .Errorf ("aborting scan due to Postman API monthly requests limit being used over %f%%" , abortScanAPIReqLimitThreshold * 100 )
30+
2831type Workspace struct {
2932 Id string `json:"id"`
3033 Name string `json:"name"`
@@ -284,22 +287,9 @@ func (c *Client) getPostmanResponseBodyBytes(ctx trContext.Context, urlString st
284287
285288 c .Metrics .apiRequests .WithLabelValues (urlString ).Inc ()
286289
287- rateLimitRemainingMonthValue := resp .Header .Get ("RateLimit-Remaining-Month" )
288- if rateLimitRemainingMonthValue == "" {
289- rateLimitRemainingMonthValue = resp .Header .Get ("X-RateLimit-Remaining-Month" )
290- }
291-
292- if rateLimitRemainingMonthValue != "" {
293- rateLimitRemainingMonth , err := strconv .Atoi (rateLimitRemainingMonthValue )
294- if err != nil {
295- ctx .Logger ().Error (err , "Couldn't convert RateLimit-Remaining-Month to an int" ,
296- "header_value" , rateLimitRemainingMonthValue ,
297- )
298- } else {
299- c .Metrics .apiMonthlyRequestsRemaining .WithLabelValues ().Set (
300- float64 (rateLimitRemainingMonth ),
301- )
302- }
290+ err = c .handleRateLimits (ctx , resp )
291+ if err != nil {
292+ return nil , err
303293 }
304294
305295 body , err := io .ReadAll (resp .Body )
@@ -315,6 +305,60 @@ func (c *Client) getPostmanResponseBodyBytes(ctx trContext.Context, urlString st
315305 return body , nil
316306}
317307
308+ // handleRateLimits processes the rate limit headers from the Postman API response
309+ // and updates the client's metrics accordingly. If the monthly rate limit usage exceeds
310+ // the set threshold, an error is returned to abort further processing.
311+ func (c * Client ) handleRateLimits (ctx trContext.Context , resp * http.Response ) error {
312+ rateLimitRemainingMonthValue := resp .Header .Get ("RateLimit-Remaining-Month" )
313+ if rateLimitRemainingMonthValue == "" {
314+ rateLimitRemainingMonthValue = resp .Header .Get ("X-RateLimit-Remaining-Month" )
315+ }
316+ if rateLimitRemainingMonthValue == "" {
317+ ctx .Logger ().V (2 ).Info ("RateLimit-Remaining-Month header not found in response" )
318+ return nil
319+ }
320+
321+ rateLimitRemainingMonth , err := strconv .Atoi (rateLimitRemainingMonthValue )
322+ if err != nil {
323+ ctx .Logger ().Error (err , "Couldn't convert RateLimit-Remaining-Month to an int" ,
324+ "header_value" , rateLimitRemainingMonthValue ,
325+ )
326+ return nil
327+ }
328+ c .Metrics .apiMonthlyRequestsRemaining .WithLabelValues ().Set (
329+ float64 (rateLimitRemainingMonth ),
330+ )
331+
332+ rateLimitTotalMonthValue := resp .Header .Get ("RateLimit-Limit-Month" )
333+ if rateLimitTotalMonthValue == "" {
334+ rateLimitTotalMonthValue = resp .Header .Get ("X-RateLimit-Limit-Month" )
335+ }
336+ if rateLimitTotalMonthValue == "" {
337+ ctx .Logger ().V (2 ).Info ("RateLimit-Limit-Month header not found in response" )
338+ return nil
339+ }
340+ rateLimitTotalMonth , err := strconv .Atoi (rateLimitTotalMonthValue )
341+ if err != nil {
342+ ctx .Logger ().Error (err , "Couldn't convert RateLimit-Limit-Month to an int" ,
343+ "header_value" , rateLimitTotalMonthValue ,
344+ )
345+ return nil
346+ }
347+
348+ if rateLimitTotalMonth == 0 {
349+ ctx .Logger ().V (2 ).Info ("RateLimit-Limit-Month is zero, cannot compute usage percentage" )
350+ return nil
351+ }
352+
353+ // failsafe to abandon scan if we are over the threshold of monthly API requests used
354+ percentageUsed := float64 (rateLimitTotalMonth - rateLimitRemainingMonth ) / float64 (rateLimitTotalMonth )
355+ if percentageUsed > abortScanAPIReqLimitThreshold {
356+ return errAbortScanDueToAPIRateLimit
357+ }
358+
359+ return nil
360+ }
361+
318362// EnumerateWorkspaces returns the workspaces for a given user (both private, public, team and personal).
319363// Consider adding additional flags to support filtering.
320364func (c * Client ) EnumerateWorkspaces (ctx trContext.Context ) ([]Workspace , error ) {
0 commit comments