Skip to content

Commit 73f0ed7

Browse files
Implement retry for Export service requests (AST-0000) (#974)
* implement retry in export * implement retry in export * fix linter * fix * resolve conversations --------- Co-authored-by: AlvoBen <[email protected]>
1 parent fb8dcf5 commit 73f0ed7

File tree

1 file changed

+64
-35
lines changed

1 file changed

+64
-35
lines changed

internal/wrappers/export-http.go

Lines changed: 64 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ func NewExportHTTPWrapper(path string) ExportWrapper {
5353
}
5454

5555
const (
56-
retryInterval = 5 * time.Second
57-
timeout = 2 * time.Minute
56+
retryInterval = 5 * time.Second
57+
timeout = 2 * time.Minute
58+
errorTimeoutMsg = "operation timed out: failed to retrieve export report"
5859
)
5960

6061
func (e *ExportHTTPWrapper) InitiateExportRequest(payload *ExportRequestPayload) (*ExportResponse, error) {
@@ -84,20 +85,20 @@ func (e *ExportHTTPWrapper) InitiateExportRequest(payload *ExportRequestPayload)
8485
if err != nil {
8586
return nil, errors.Wrapf(err, "failed to parse response body")
8687
}
87-
resp.Body.Close()
88+
_ = resp.Body.Close()
8889
return &model, nil
8990
case http.StatusBadRequest:
9091
if time.Now().After(endTime) {
9192
log.Printf("Timeout reached after %d attempts. Last response status code: %d", retryCount+1, resp.StatusCode)
92-
resp.Body.Close()
93+
_ = resp.Body.Close()
9394
return nil, errors.Errorf("failed to initiate export request - response status code %d", resp.StatusCode)
9495
}
9596
retryCount++
9697
logger.PrintfIfVerbose("Received 400 Bad Request. Retrying in %v... (attempt %d/%d)", retryInterval, retryCount, maxRetries)
9798
time.Sleep(retryInterval)
9899
default:
99100
logger.PrintfIfVerbose("Received unexpected status code %d", resp.StatusCode)
100-
resp.Body.Close()
101+
_ = resp.Body.Close()
101102
return nil, errors.Errorf("response status code %d", resp.StatusCode)
102103
}
103104
}
@@ -107,27 +108,37 @@ func (e *ExportHTTPWrapper) GetExportReportStatus(reportID string) (*ExportPolli
107108
clientTimeout := viper.GetUint(commonParams.ClientTimeoutKey)
108109
path := fmt.Sprintf("%s/%s", e.path, "requests")
109110
params := map[string]string{"returnUrl": "true", "exportId": reportID}
110-
resp, err := SendPrivateHTTPRequestWithQueryParams(http.MethodGet, path, params, nil, clientTimeout)
111-
if err != nil {
112-
return nil, err
113-
}
114111

115-
defer func() {
116-
_ = resp.Body.Close()
117-
}()
112+
start := time.Now()
118113

119-
decoder := json.NewDecoder(resp.Body)
114+
for {
115+
if time.Since(start) > timeout {
116+
return nil, errors.New(errorTimeoutMsg)
117+
}
120118

121-
switch resp.StatusCode {
122-
case http.StatusOK:
123-
model := ExportPollingResponse{}
124-
err = decoder.Decode(&model)
119+
resp, err := SendPrivateHTTPRequestWithQueryParams(http.MethodGet, path, params, nil, clientTimeout)
125120
if err != nil {
126-
return nil, errors.Wrapf(err, "failed to parse response body")
121+
time.Sleep(retryInterval)
122+
continue
123+
}
124+
125+
decoder := json.NewDecoder(resp.Body)
126+
127+
switch resp.StatusCode {
128+
case http.StatusOK:
129+
model := ExportPollingResponse{}
130+
if err = decoder.Decode(&model); err != nil {
131+
_ = resp.Body.Close()
132+
return nil, errors.Wrapf(err, "failed to parse response body")
133+
}
134+
return &model, nil
135+
case http.StatusNotFound:
136+
_ = resp.Body.Close()
137+
time.Sleep(time.Second)
138+
default:
139+
_ = resp.Body.Close()
140+
return nil, errors.Errorf("response status code %d", resp.StatusCode)
127141
}
128-
return &model, nil
129-
default:
130-
return nil, errors.Errorf("response status code %d", resp.StatusCode)
131142
}
132143
}
133144

@@ -151,7 +162,11 @@ func (e *ExportHTTPWrapper) DownloadExportReport(reportID, targetFile string) er
151162
if err != nil {
152163
return errors.Wrapf(err, "Failed to create file %s", targetFile)
153164
}
154-
defer file.Close()
165+
166+
defer func(file *os.File) {
167+
_ = file.Close()
168+
}(file)
169+
155170
size, err := io.Copy(file, resp.Body)
156171
if err != nil {
157172
return errors.Wrapf(err, "Failed to write file %s", targetFile)
@@ -162,30 +177,44 @@ func (e *ExportHTTPWrapper) DownloadExportReport(reportID, targetFile string) er
162177
}
163178

164179
func (e *ExportHTTPWrapper) GetScaPackageCollectionExport(fileURL string) (*ScaPackageCollectionExport, error) {
180+
const bomPrefix = "\xef\xbb\xbf"
181+
165182
accessToken, err := GetAccessToken()
166183
if err != nil {
167-
return nil, err
184+
return nil, errors.Wrap(err, "failed to get access token")
168185
}
169186

170-
resp, err := SendHTTPRequestByFullURL(http.MethodGet, fileURL, http.NoBody, true, viper.GetUint(commonParams.ClientTimeoutKey), accessToken, true)
171-
if err != nil {
172-
return nil, err
187+
start := time.Now()
188+
var resp *http.Response
189+
190+
for {
191+
if time.Since(start) > timeout {
192+
return nil, errors.New(errorTimeoutMsg)
193+
}
194+
195+
resp, err = SendHTTPRequestByFullURL(http.MethodGet, fileURL, http.NoBody, true, viper.GetUint(commonParams.ClientTimeoutKey), accessToken, true)
196+
if err == nil && resp.StatusCode == http.StatusOK {
197+
break
198+
}
199+
_ = resp.Body.Close()
200+
time.Sleep(retryInterval)
173201
}
174-
defer resp.Body.Close()
202+
203+
defer func() {
204+
_ = resp.Body.Close()
205+
}()
175206

176207
body, err := io.ReadAll(resp.Body)
177208
if err != nil {
178-
return nil, err
209+
return nil, errors.Wrap(err, "failed to read response body")
179210
}
211+
body = bytes.TrimPrefix(body, []byte(bomPrefix))
180212

181-
// Remove BOM if present
182-
body = bytes.TrimPrefix(body, []byte("\xef\xbb\xbf"))
183-
184-
var scaPackageCollection ScaPackageCollectionExport
185-
if err := json.Unmarshal(body, &scaPackageCollection); err != nil {
186-
return nil, err
213+
scaPackageCollection := &ScaPackageCollectionExport{}
214+
if err = json.Unmarshal(body, scaPackageCollection); err != nil {
215+
return nil, errors.Wrap(err, "failed to unmarshal response body")
187216
}
188217

189218
logger.PrintIfVerbose("Retrieved SCA package collection export successfully")
190-
return &scaPackageCollection, nil
219+
return scaPackageCollection, nil
191220
}

0 commit comments

Comments
 (0)