Skip to content

Commit

Permalink
handle export bad request
Browse files Browse the repository at this point in the history
  • Loading branch information
BenAlvo1 committed Aug 13, 2024
1 parent d17d61d commit 9d67dfe
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 19 deletions.
57 changes: 38 additions & 19 deletions internal/wrappers/export-http.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"log"
"net/http"
"os"
"time"

"github.com/checkmarx/ast-cli/internal/logger"
commonParams "github.com/checkmarx/ast-cli/internal/params"
Expand Down Expand Up @@ -51,34 +52,52 @@ func NewExportHTTPWrapper(path string) ExportWrapper {
}
}

const (
retryInterval = 5 * time.Second
timeout = 2 * time.Minute
)

func (e *ExportHTTPWrapper) InitiateExportRequest(payload *ExportRequestPayload) (*ExportResponse, error) {
clientTimeout := viper.GetUint(commonParams.ClientTimeoutKey)
params, err := json.Marshal(payload)
if err != nil {
return nil, errors.Wrapf(err, "Failed to parse request body")
return nil, errors.Wrapf(err, "failed to parse request body")
}
path := fmt.Sprintf("%s/%s", e.path, "requests")
resp, err := SendHTTPRequestWithJSONContentType(http.MethodPost, path, bytes.NewBuffer(params), true, clientTimeout)
if err != nil {
return nil, err
}
defer func() {
_ = resp.Body.Close()
}()

switch resp.StatusCode {
case http.StatusAccepted:
model := ExportResponse{}
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(&model)
endTime := time.Now().Add(timeout)
retryCount := 0

for {
logger.PrintfIfVerbose("Sending export request, attempt %d", retryCount+1)
resp, err := SendHTTPRequestWithJSONContentType(http.MethodPost, path, bytes.NewBuffer(params), true, clientTimeout)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse response body")
return nil, err
}
defer resp.Body.Close()

Check failure on line 77 in internal/wrappers/export-http.go

View workflow job for this annotation

GitHub Actions / lint

deferInLoop: Possible resource leak, 'defer' is called in the 'for' loop (gocritic)

switch resp.StatusCode {
case http.StatusAccepted:
logger.PrintIfVerbose("Received 202 Accepted response from server.")
model := ExportResponse{}
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(&model)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse response body")
}
return &model, nil
case http.StatusBadRequest:
if time.Now().After(endTime) {
log.Printf("Timeout reached after %d attempts. Last response status code: %d", retryCount+1, resp.StatusCode)
return nil, errors.Errorf("failed to initiate export request - response status code %d", resp.StatusCode)
}
retryCount++
logger.PrintfIfVerbose("Received 400 Bad Request. Retrying in %v... (attempt %d/%d)", retryInterval, retryCount, maxRetries)
time.Sleep(retryInterval)
default:
logger.PrintfIfVerbose("Received unexpected status code %d", resp.StatusCode)
return nil, errors.Errorf("response status code %d", resp.StatusCode)
}
return &model, nil
case http.StatusBadRequest:
return nil, errors.Errorf("SBOM report is currently in beta mode and not available for this tenant type")
default:
return nil, errors.Errorf("response status code %d", resp.StatusCode)
}
}

Expand Down
26 changes: 26 additions & 0 deletions test/integration/scan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,13 @@ import (
errorConstants "github.com/checkmarx/ast-cli/internal/constants/errors"
exitCodes "github.com/checkmarx/ast-cli/internal/constants/exit-codes"
"github.com/checkmarx/ast-cli/internal/params"
"github.com/checkmarx/ast-cli/internal/services"
"github.com/checkmarx/ast-cli/internal/wrappers"
"github.com/checkmarx/ast-cli/internal/wrappers/configuration"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/spf13/viper"
asserts "github.com/stretchr/testify/assert"
"gotest.tools/assert"
)

Expand Down Expand Up @@ -1838,3 +1842,25 @@ func validateCheckmarxDomains(t *testing.T, usedDomainsInTests []string) {
assert.Assert(t, slices.Contains(usedDomainsInTests, domain), "Domain "+domain+" not found in used domains")
}
}

func TestCreateAsyncScan_CallExportServiceBeforeScanFinishWithRetry_Success(t *testing.T) {
createASTIntegrationTestCommand(t)
configuration.LoadConfiguration()
args := []string{
"scan", "create",
flag(params.ProjectName), generateRandomProjectNameForScan(),
flag(params.SourcesFlag), "data/empty-folder.zip",
flag(params.ScanTypes), "sca",
flag(params.BranchFlag), "main",
flag(params.AsyncFlag),
flag(params.ScanInfoFormatFlag), printer.FormatJSON,
}
scanID, _ := executeCreateScan(t, args)
exportRes, err := services.GetExportPackage(wrappers.NewExportHTTPWrapper("api/sca/export"), scanID)
asserts.Nil(t, err)
assert.Assert(t, exportRes != nil, "Export response should not be nil")
}

func generateRandomProjectNameForScan() string {
return fmt.Sprintf("ast-cli-scan-", uuid.New().String())

Check failure on line 1865 in test/integration/scan_test.go

View workflow job for this annotation

GitHub Actions / integration-tests

fmt.Sprintf call has arguments but no formatting directives
}

0 comments on commit 9d67dfe

Please sign in to comment.