77 "strconv"
88 "strings"
99 "sync/atomic"
10+ "time"
1011
1112 resty "github.com/go-resty/resty/v2"
1213 "github.com/prometheus/client_golang/prometheus"
@@ -32,6 +33,8 @@ type AzureDevopsClient struct {
3233 semaphore chan bool
3334 concurrency int64
3435
36+ delayUntil * time.Time
37+
3538 LimitProject int64
3639 LimitBuildsPerProject int64
3740 LimitBuildsPerDefinition int64
@@ -123,7 +126,12 @@ func (c *AzureDevopsClient) rest() *resty.Client {
123126 c .restClient .SetHeader ("Accept" , "application/json" )
124127 c .restClient .SetBasicAuth ("" , * c .accessToken )
125128 c .restClient .SetRetryCount (c .RequestRetries )
126- c .restClient .OnBeforeRequest (c .restOnBeforeRequest )
129+ if c .delayUntil != nil {
130+ c .restClient .OnBeforeRequest (c .restOnBeforeRequestDelay )
131+ } else {
132+ c .restClient .OnBeforeRequest (c .restOnBeforeRequest )
133+ }
134+
127135 c .restClient .OnAfterResponse (c .restOnAfterResponse )
128136
129137 }
@@ -157,6 +165,19 @@ func (c *AzureDevopsClient) concurrencyUnlock() {
157165 <- c .semaphore
158166}
159167
168+ // PreRequestHook is a resty hook that is called before every request
169+ // It checks that the delay is ok before requesting
170+ func (c * AzureDevopsClient ) restOnBeforeRequestDelay (client * resty.Client , request * resty.Request ) (err error ) {
171+ atomic .AddUint64 (& c .RequestCount , 1 )
172+ if c .delayUntil != nil {
173+ if time .Now ().Before (* c .delayUntil ) {
174+ time .Sleep (time .Until (* c .delayUntil ))
175+ }
176+ c .delayUntil = nil
177+ }
178+ return
179+ }
180+
160181func (c * AzureDevopsClient ) restOnBeforeRequest (client * resty.Client , request * resty.Request ) (err error ) {
161182 atomic .AddUint64 (& c .RequestCount , 1 )
162183 return
@@ -187,6 +208,14 @@ func (c *AzureDevopsClient) checkResponse(response *resty.Response, err error) e
187208 return err
188209 }
189210 if response != nil {
211+ // check delay from usage quota
212+ if d := response .Header ().Get ("Retry-After" ); d != "" {
213+ // convert string to int to time.Duration
214+ if dInt , err := strconv .Atoi (d ); err != nil {
215+ dD := time .Now ().Add (time .Duration (dInt ) * time .Second )
216+ c .delayUntil = & dD
217+ }
218+ }
190219 // check status code
191220 statusCode := response .StatusCode ()
192221 if statusCode != 200 {
0 commit comments