@@ -53,8 +53,9 @@ func NewExportHTTPWrapper(path string) ExportWrapper {
5353}
5454
5555const (
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
6061func (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
164179func (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