Skip to content

Commit a7caa75

Browse files
feat: use workspace client (#24)
1 parent aa7e17d commit a7caa75

File tree

24 files changed

+1237
-117
lines changed

24 files changed

+1237
-117
lines changed

.circleci/config.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ jobs:
3434
- run:
3535
name: Run unit tests
3636
command: make test
37+
smoke_test:
38+
executor: default
39+
steps:
40+
- checkout
41+
- run:
42+
name: Install tools
43+
command: make tools
44+
- run:
45+
name: Run smoke tests
46+
command: make smoke-test
3747
build:
3848
executor: default
3949
steps:
@@ -67,9 +77,15 @@ workflows:
6777
name: Unit tests
6878
requires:
6979
- Lint & Format
80+
- smoke_test:
81+
name: Smoke tests
82+
context:
83+
- code-client-go-smoke-tests-token
84+
requires:
85+
- Unit tests
7086
- build:
7187
name: Build
7288
requires:
73-
- Unit tests
89+
- Smoke tests
7490
- Security Scans
7591
- Scan repository for secrets

CONTRIBUTING.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ make test
5959

6060
If writing unit tests, use the mocks generated by [GoMock](https://github.com/golang/mock) by running `make generate`.
6161

62-
If writing `pact` or integration tests, use the test implementations in [./internal/util/testutil](./internal/util/testutil).
62+
If writing `pact`, integration, or smoke tests, use the test implementations in [./internal/util/testutil](./internal/util/testutil).
63+
64+
The organisation used by the smoke tests is `ide-consistent-ignores-test` in [https://app.dev.snyk.io](https://app.dev.snyk.io) and we are authenticating using a service account api key.
6365

6466
If you've changed any of the interfaces you may need to re-run `make generate` to generate the mocks again.
6567

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ test:
5454
.PHONY: testv
5555
testv:
5656
@echo "Testing verbosely..."
57-
@go test -v ./...
57+
@go test -v
58+
59+
.PHONY: smoke-test
60+
smoke-test:
61+
@go test -run="Test_SmokeScan"
5862

5963
.PHONY: generate
6064
generate: $(TOOLS_BIN)/go/mockgen $(TOOLS_BIN)/go/oapi-codegen

README.md

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -38,44 +38,30 @@ The HTTP client exposes a `DoCall` function.
3838

3939
Implement the `http.Config` interface to configure the Snyk Code API client from applications.
4040

41-
### Snyk Code Client
42-
43-
Use the Snyk Code Client to make calls to the DeepCode API using the `httpClient` HTTP client created above.
44-
45-
```go
46-
snykCode := deepcode.NewSnykCodeClient(logger, httpClient, testutil.NewTestInstrumentor())
47-
```
48-
49-
The Snyk Code Client exposes the following functions:
50-
- `GetFilters`
51-
- `CreateBundle`
52-
- `ExtendBundle`
53-
54-
### Bundle Manager
55-
56-
Use the Bundle Manager to create bundles using the `snykCode` Snyk Code Client created above and then to extend it by uploading more files to it.
57-
58-
```go
59-
bundleManager := bundle.NewBundleManager(logger, snykCode, testutil.NewTestInstrumentor(), testutil.NewTestCodeInstrumentor())
60-
```
61-
62-
The Bundle Manager exposes the following functions:
63-
- `Create`
64-
- `Upload`
65-
6641
### Code Scanner
6742

6843
Use the Code Scanner to trigger a scan for a Snyk Code workspace using the Bundle Manager created above.
6944
The Code Scanner exposes a `UploadAndAnalyze` function, which can be used like this:
7045

7146
```go
72-
codeScanner := codeclient.NewCodeScanner(
73-
bundleManager,
74-
testutil.NewTestInstrumentor(),
75-
testutil.NewTestErrorReporter(),
47+
import (
48+
"net/http"
49+
50+
"github.com/rs/zerolog"
51+
code "github.com/snyk/code-client-go"
52+
)
53+
54+
logger := zerlog.NewLogger(...)
55+
config := newConfigForMyApp()
56+
57+
codeScanner := code.NewCodeScanner(
58+
httpClient,
59+
config,
60+
codeInstrumentor,
61+
codeErrorReporter,
7662
logger,
7763
)
78-
codeScanner.UploadAndAnalyze(context.Background(), "path/to/workspace", channelForWalkingFiles, changedFiles)
64+
code.UploadAndAnalyze(context.Background(), requestId, "path/to/workspace", channelForWalkingFiles, changedFiles)
7965
```
8066

8167

config/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,7 @@ type Config interface {
1616
// SnykCodeApi returns the Snyk Code API URL configured to run against, which could be
1717
// the one used by the Local Code Engine.
1818
SnykCodeApi() string
19+
20+
// SnykApi returns the Snyk REST API URL configured to run against,
21+
SnykApi() string
1922
}

config/mocks/config.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

http/http.go

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
package http
1919

2020
import (
21-
"errors"
21+
"bytes"
22+
"io"
2223
"net/http"
2324
"time"
2425

@@ -78,19 +79,14 @@ func (s *httpClient) Do(req *http.Request) (response *http.Response, err error)
7879
return nil, err // no retries for errors
7980
}
8081

81-
err = s.checkResponseCode(response)
82-
if err != nil {
83-
if retryErrorCodes[response.StatusCode] {
84-
s.logger.Debug().Err(err).Str("method", req.Method).Int("attempts done", i+1).Msg("retrying")
85-
if i < retryCount-1 {
86-
time.Sleep(5 * time.Second)
87-
continue
88-
}
89-
// return the error on last try
90-
return nil, err
82+
if retryErrorCodes[response.StatusCode] {
83+
s.logger.Debug().Err(err).Str("method", req.Method).Int("attempts done", i+1).Msg("retrying")
84+
if i < retryCount-1 {
85+
time.Sleep(5 * time.Second)
86+
continue
9187
}
92-
return nil, err
9388
}
89+
9490
// no error, we can break the retry loop
9591
break
9692
}
@@ -99,7 +95,18 @@ func (s *httpClient) Do(req *http.Request) (response *http.Response, err error)
9995

10096
func (s *httpClient) httpCall(req *http.Request) (*http.Response, error) {
10197
log := s.logger.With().Str("method", "http.httpCall").Logger()
98+
99+
// store the request body so that after retrying it can be read again
100+
var copyReqBody io.ReadCloser
101+
if req.Body != nil {
102+
buf, _ := io.ReadAll(req.Body)
103+
reqBody := io.NopCloser(bytes.NewBuffer(buf))
104+
copyReqBody = io.NopCloser(bytes.NewBuffer(buf))
105+
req.Body = reqBody
106+
}
102107
response, err := s.clientFactory().Do(req)
108+
req.Body = copyReqBody
109+
103110
if err != nil {
104111
log.Error().Err(err).Msg("got http error")
105112
s.errorReporter.CaptureError(err, observability.ErrorReporterOptions{ErrorDiagnosticPath: req.RequestURI})
@@ -108,10 +115,3 @@ func (s *httpClient) httpCall(req *http.Request) (*http.Response, error) {
108115

109116
return response, nil
110117
}
111-
112-
func (s *httpClient) checkResponseCode(r *http.Response) error {
113-
if r.StatusCode >= 200 && r.StatusCode <= 299 {
114-
return nil
115-
}
116-
return errors.New("Unexpected response code: " + r.Status)
117-
}

http/http_test.go

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616
package http_test
1717

1818
import (
19+
"fmt"
20+
"io"
1921
"net/http"
22+
"strings"
2023
"testing"
2124

2225
"github.com/golang/mock/gomock"
@@ -36,8 +39,17 @@ type dummyTransport struct {
3639
calls int
3740
}
3841

39-
func (d *dummyTransport) RoundTrip(_ *http.Request) (*http.Response, error) {
42+
func (d *dummyTransport) RoundTrip(req *http.Request) (*http.Response, error) {
4043
d.calls++
44+
if req.Body != nil {
45+
body, err := io.ReadAll(req.Body)
46+
if err != nil {
47+
return nil, err
48+
}
49+
if string(body) == "" {
50+
return nil, fmt.Errorf("body is empty")
51+
}
52+
}
4153
return &http.Response{
4254
StatusCode: d.responseCode,
4355
Status: d.status,
@@ -64,8 +76,35 @@ func TestSnykCodeBackendService_DoCall_shouldRetry(t *testing.T) {
6476
require.NoError(t, err)
6577

6678
s := codeClientHTTP.NewHTTPClient(newLogger(t), dummyClientFactory, mockInstrumentor, mockErrorReporter)
67-
_, err = s.Do(req)
68-
assert.Error(t, err)
79+
res, err := s.Do(req)
80+
assert.NoError(t, err)
81+
assert.NotNil(t, res)
82+
assert.Equal(t, 3, d.calls)
83+
}
84+
85+
func TestSnykCodeBackendService_DoCall_shouldRetryWithARequestBody(t *testing.T) {
86+
d := &dummyTransport{responseCode: 502, status: "502 Bad Gateway"}
87+
dummyClientFactory := func() *http.Client {
88+
return &http.Client{
89+
Transport: d,
90+
}
91+
}
92+
93+
ctrl := gomock.NewController(t)
94+
mockSpan := mocks.NewMockSpan(ctrl)
95+
mockSpan.EXPECT().GetTraceId().AnyTimes()
96+
mockInstrumentor := mocks.NewMockInstrumentor(ctrl)
97+
mockInstrumentor.EXPECT().StartSpan(gomock.Any(), gomock.Any()).Return(mockSpan).Times(1)
98+
mockInstrumentor.EXPECT().Finish(gomock.Any()).Times(1)
99+
mockErrorReporter := mocks.NewMockErrorReporter(ctrl)
100+
101+
req, err := http.NewRequest(http.MethodGet, "https://httpstat.us/500", io.NopCloser(strings.NewReader("body")))
102+
require.NoError(t, err)
103+
104+
s := codeClientHTTP.NewHTTPClient(newLogger(t), dummyClientFactory, mockInstrumentor, mockErrorReporter)
105+
res, err := s.Do(req)
106+
assert.NoError(t, err)
107+
assert.NotNil(t, res)
69108
assert.Equal(t, 3, d.calls)
70109
}
71110

0 commit comments

Comments
 (0)