Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

# code-client-go

A library that exposes scanning capabilities for Snyk Code that can be used in the [Snyk CLI](https://github.com/snyk/cli) as well as Snyk IDE plugins using the [Snyk Language Server](https://github.com/snyk/snyk-ls).
Expand Down Expand Up @@ -35,13 +36,12 @@ The HTTP client exposes a `DoCall` function.

### Configuration

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

### Snyk Code Client

Use the Snyk Code Client to make calls to the DeepCode API using the `httpClient` HTTP client created above.


```go
snykCode := deepcode.NewSnykCodeClient(logger, httpClient, testutil.NewTestInstrumentor())
```
Expand All @@ -56,7 +56,7 @@ The Snyk Code Client exposes the following functions:
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.

```go
bundleManager := bundle.NewBundleManager(snykCode, testutil.NewTestInstrumentor(), testutil.NewTestCodeInstrumentor())
bundleManager := bundle.NewBundleManager(logger, snykCode, testutil.NewTestInstrumentor(), testutil.NewTestCodeInstrumentor())
```

The Bundle Manager exposes the following functions:
Expand All @@ -65,18 +65,19 @@ The Bundle Manager exposes the following functions:

### Code Scanner

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

```go
codeScanner := codeclient.NewCodeScanner(
bundleManager,
testutil.NewTestInstrumentor(),
testutil.NewTestCodeInstrumentor(),
testutils.NewTestAnalytics(),
testutil.NewTestErrorReporter(),
logger,
)
codeScanner.UploadAndAnalyze(context.Background(), "path/to/workspace", channelForWalkingFiles, changedFiles)
```

The Code Scanner exposes a `UploadAndAnalyze` function.

### Observability

Expand Down
10 changes: 5 additions & 5 deletions bundle/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (

//go:generate mockgen -destination=mocks/bundle.go -source=bundle.go -package mocks
type Bundle interface {
UploadBatch(ctx context.Context, host string, batch *Batch) error
UploadBatch(ctx context.Context, batch *Batch) error
GetBundleHash() string
GetRootPath() string
GetRequestId() string
Expand Down Expand Up @@ -95,19 +95,19 @@ func (b *deepCodeBundle) GetMissingFiles() []string {
return b.missingFiles
}

func (b *deepCodeBundle) UploadBatch(ctx context.Context, host string, batch *Batch) error {
err := b.extendBundle(ctx, host, batch)
func (b *deepCodeBundle) UploadBatch(ctx context.Context, batch *Batch) error {
err := b.extendBundle(ctx, batch)
if err != nil {
return err
}
b.batches = append(b.batches, batch)
return nil
}

func (b *deepCodeBundle) extendBundle(ctx context.Context, host string, uploadBatch *Batch) error {
func (b *deepCodeBundle) extendBundle(ctx context.Context, uploadBatch *Batch) error {
var err error
if uploadBatch.hasContent() {
b.bundleHash, b.missingFiles, err = b.SnykCode.ExtendBundle(ctx, host, b.bundleHash, uploadBatch.documents, []string{})
b.bundleHash, b.missingFiles, err = b.SnykCode.ExtendBundle(ctx, b.bundleHash, uploadBatch.documents, []string{})
b.logger.Debug().Str("requestId", b.requestId).Interface("MissingFiles", b.missingFiles).Msg("extended deepCodeBundle on backend")
}

Expand Down
14 changes: 5 additions & 9 deletions bundle/bundle_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ type bundleManager struct {
//go:generate mockgen -destination=mocks/bundle_manager.go -source=bundle_manager.go -package mocks
type BundleManager interface {
Create(ctx context.Context,
host string,
requestId string,
rootPath string,
filePaths <-chan string,
Expand All @@ -51,7 +50,6 @@ type BundleManager interface {

Upload(
ctx context.Context,
host string,
originalBundle Bundle,
files map[string]deepcode.BundleFile,
) (Bundle, error)
Expand All @@ -74,7 +72,6 @@ func NewBundleManager(
}

func (b *bundleManager) Create(ctx context.Context,
host string,
requestId string,
rootPath string,
filePaths <-chan string,
Expand All @@ -93,7 +90,7 @@ func (b *bundleManager) Create(ctx context.Context,
return bundle, err // The cancellation error should be handled by the calling function
}
var supported bool
supported, err = b.IsSupported(span.Context(), host, absoluteFilePath)
supported, err = b.IsSupported(span.Context(), absoluteFilePath)
if err != nil {
return bundle, err
}
Expand Down Expand Up @@ -134,7 +131,7 @@ func (b *bundleManager) Create(ctx context.Context,
var bundleHash string
var missingFiles []string
if len(fileHashes) > 0 {
bundleHash, missingFiles, err = b.SnykCode.CreateBundle(span.Context(), host, fileHashes)
bundleHash, missingFiles, err = b.SnykCode.CreateBundle(span.Context(), fileHashes)
}
bundle = NewBundle(
b.SnykCode,
Expand All @@ -153,7 +150,6 @@ func (b *bundleManager) Create(ctx context.Context,

func (b *bundleManager) Upload(
ctx context.Context,
host string,
bundle Bundle,
files map[string]deepcode.BundleFile,
) (Bundle, error) {
Expand All @@ -172,7 +168,7 @@ func (b *bundleManager) Upload(
if err := ctx.Err(); err != nil {
return bundle, err
}
err := bundle.UploadBatch(s.Context(), host, batch)
err := bundle.UploadBatch(s.Context(), batch)
if err != nil {
return bundle, err
}
Expand Down Expand Up @@ -214,9 +210,9 @@ func (b *bundleManager) groupInBatches(
return batches
}

func (b *bundleManager) IsSupported(ctx context.Context, host string, file string) (bool, error) {
func (b *bundleManager) IsSupported(ctx context.Context, file string) (bool, error) {
if b.supportedExtensions.Size() == 0 && b.supportedConfigFiles.Size() == 0 {
filters, err := b.SnykCode.GetFilters(ctx, host)
filters, err := b.SnykCode.GetFilters(ctx)
if err != nil {
b.logger.Error().Err(err).Msg("could not get filters")
return false, err
Expand Down
54 changes: 23 additions & 31 deletions bundle/bundle_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ func Test_Create(t *testing.T) {
mockSpan := mocks.NewMockSpan(ctrl)
mockSpan.EXPECT().Context().AnyTimes()
mockSnykCodeClient := mocks2.NewMockSnykCodeClient(ctrl)
mockSnykCodeClient.EXPECT().GetFilters(gomock.Any(), "testHost").Return(deepcode2.FiltersResponse{
mockSnykCodeClient.EXPECT().GetFilters(gomock.Any()).Return(deepcode2.FiltersResponse{
ConfigFiles: []string{},
Extensions: []string{".java"},
}, nil)
mockSnykCodeClient.EXPECT().CreateBundle(gomock.Any(), "testHost", map[string]string{
mockSnykCodeClient.EXPECT().CreateBundle(gomock.Any(), map[string]string{
"file.java": "386f1997f6da5133a0f75c347d5cdff15a428b817231278e2509832c1a80b3ea",
}).Times(1)
mockInstrumentor := mocks.NewMockInstrumentor(ctrl)
Expand All @@ -63,7 +63,6 @@ func Test_Create(t *testing.T) {

var bundleManager = bundle.NewBundleManager(newLogger(t), mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
bundle, err := bundleManager.Create(context.Background(),
"testHost",
"testRequestId",
dir,
sliceToChannel([]string{file}),
Expand All @@ -79,7 +78,7 @@ func Test_Create(t *testing.T) {
mockSpan := mocks.NewMockSpan(ctrl)
mockSpan.EXPECT().Context().AnyTimes()
mockSnykCodeClient := mocks2.NewMockSnykCodeClient(ctrl)
mockSnykCodeClient.EXPECT().GetFilters(gomock.Any(), "testHost").Return(deepcode2.FiltersResponse{
mockSnykCodeClient.EXPECT().GetFilters(gomock.Any()).Return(deepcode2.FiltersResponse{
ConfigFiles: []string{},
Extensions: []string{".java"},
}, nil)
Expand All @@ -96,7 +95,6 @@ func Test_Create(t *testing.T) {

var bundleManager = bundle.NewBundleManager(newLogger(t), mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
bundle, err := bundleManager.Create(context.Background(),
"testHost",
"testRequestId",
dir,
sliceToChannel([]string{file}),
Expand All @@ -113,7 +111,7 @@ func Test_Create(t *testing.T) {
mockSpan := mocks.NewMockSpan(ctrl)
mockSpan.EXPECT().Context().AnyTimes()
mockSnykCodeClient := mocks2.NewMockSnykCodeClient(ctrl)
mockSnykCodeClient.EXPECT().GetFilters(gomock.Any(), "testHost").Return(deepcode2.FiltersResponse{
mockSnykCodeClient.EXPECT().GetFilters(gomock.Any()).Return(deepcode2.FiltersResponse{
ConfigFiles: []string{},
Extensions: []string{".java"},
}, nil)
Expand All @@ -134,7 +132,6 @@ func Test_Create(t *testing.T) {

var bundleManager = bundle.NewBundleManager(newLogger(t), mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
bundle, err := bundleManager.Create(context.Background(),
"testHost",
"testRequestId",
dir,
sliceToChannel([]string{file}),
Expand All @@ -151,7 +148,7 @@ func Test_Create(t *testing.T) {
mockSpan := mocks.NewMockSpan(ctrl)
mockSpan.EXPECT().Context().AnyTimes()
mockSnykCodeClient := mocks2.NewMockSnykCodeClient(ctrl)
mockSnykCodeClient.EXPECT().GetFilters(gomock.Any(), "testHost").Return(deepcode2.FiltersResponse{
mockSnykCodeClient.EXPECT().GetFilters(gomock.Any()).Return(deepcode2.FiltersResponse{
ConfigFiles: []string{},
Extensions: []string{".java"},
}, nil)
Expand All @@ -171,7 +168,6 @@ func Test_Create(t *testing.T) {
require.NoError(t, err)
var bundleManager = bundle.NewBundleManager(newLogger(t), mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
bundle, err := bundleManager.Create(context.Background(),
"testHost",
"testRequestId",
dir,
sliceToChannel([]string{file}),
Expand All @@ -186,11 +182,11 @@ func Test_Create(t *testing.T) {
mockSpan := mocks.NewMockSpan(ctrl)
mockSpan.EXPECT().Context().AnyTimes()
mockSnykCodeClient := mocks2.NewMockSnykCodeClient(ctrl)
mockSnykCodeClient.EXPECT().GetFilters(gomock.Any(), "testHost").Return(deepcode2.FiltersResponse{
mockSnykCodeClient.EXPECT().GetFilters(gomock.Any()).Return(deepcode2.FiltersResponse{
ConfigFiles: []string{".test"},
Extensions: []string{},
}, nil)
mockSnykCodeClient.EXPECT().CreateBundle(gomock.Any(), "testHost", map[string]string{
mockSnykCodeClient.EXPECT().CreateBundle(gomock.Any(), map[string]string{
".test": "9c05690c5b8e22df259431c95df33d01267f799de6810382ada1a9ff1b89710e",
}).Times(1)
mockInstrumentor := mocks.NewMockInstrumentor(ctrl)
Expand All @@ -205,7 +201,6 @@ func Test_Create(t *testing.T) {

var bundleManager = bundle.NewBundleManager(newLogger(t), mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
bundle, err := bundleManager.Create(context.Background(),
"testHost",
"testRequestId",
tempDir,
sliceToChannel([]string{file}),
Expand All @@ -220,11 +215,11 @@ func Test_Create(t *testing.T) {
mockSpan := mocks.NewMockSpan(ctrl)
mockSpan.EXPECT().Context().AnyTimes()
mockSnykCodeClient := mocks2.NewMockSnykCodeClient(ctrl)
mockSnykCodeClient.EXPECT().GetFilters(gomock.Any(), "testHost").Return(deepcode2.FiltersResponse{
mockSnykCodeClient.EXPECT().GetFilters(gomock.Any()).Return(deepcode2.FiltersResponse{
ConfigFiles: []string{},
Extensions: []string{".java"},
}, nil)
mockSnykCodeClient.EXPECT().CreateBundle(gomock.Any(), "testHost", map[string]string{
mockSnykCodeClient.EXPECT().CreateBundle(gomock.Any(), map[string]string{
"path/to/file1.java": "9c05690c5b8e22df259431c95df33d01267f799de6810382ada1a9ff1b89710e",
"path/with%20spaces/file2.java": "9c05690c5b8e22df259431c95df33d01267f799de6810382ada1a9ff1b89710e",
}).Times(1)
Expand Down Expand Up @@ -254,7 +249,6 @@ func Test_Create(t *testing.T) {

var bundleManager = bundle.NewBundleManager(newLogger(t), mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
bundle, err := bundleManager.Create(context.Background(),
"testHost",
"testRequestId",
tempDir,
sliceToChannel(filesFullPaths),
Expand All @@ -279,7 +273,7 @@ func Test_Upload(t *testing.T) {
mockSpan := mocks.NewMockSpan(ctrl)
mockSpan.EXPECT().Context().AnyTimes()
mockSnykCodeClient := mocks2.NewMockSnykCodeClient(ctrl)
mockSnykCodeClient.EXPECT().ExtendBundle(gomock.Any(), "testHost", "bundleHash", gomock.Len(1), []string{}).Times(1)
mockSnykCodeClient.EXPECT().ExtendBundle(gomock.Any(), "bundleHash", gomock.Len(1), []string{}).Times(1)
mockInstrumentor := mocks.NewMockInstrumentor(ctrl)
mockInstrumentor.EXPECT().StartSpan(gomock.Any(), gomock.Any()).Return(mockSpan).Times(2)
mockInstrumentor.EXPECT().Finish(gomock.Any()).Times(2)
Expand All @@ -291,7 +285,6 @@ func Test_Upload(t *testing.T) {
bundleFileMap[documentURI] = bundleFile

_, err := bundleManager.Upload(context.Background(),
"testHost",
bundle.NewBundle(mockSnykCodeClient, mockInstrumentor, mockErrorReporter, &logger, "bundleHash", "testRequestId", "", bundleFileMap, []string{}, []string{documentURI}),
bundleFileMap)
assert.NoError(t, err)
Expand All @@ -302,8 +295,8 @@ func Test_Upload(t *testing.T) {
mockSpan := mocks.NewMockSpan(ctrl)
mockSpan.EXPECT().Context().AnyTimes()
mockSnykCodeClient := mocks2.NewMockSnykCodeClient(ctrl)
mockSnykCodeClient.EXPECT().ExtendBundle(gomock.Any(), "testHost", "bundleHash", gomock.Len(3), []string{}).Return("newBundleHash", []string{}, nil).Times(1)
mockSnykCodeClient.EXPECT().ExtendBundle(gomock.Any(), "testHost", "newBundleHash", gomock.Len(2), []string{}).Return("newerBundleHash", []string{}, nil).Times(1)
mockSnykCodeClient.EXPECT().ExtendBundle(gomock.Any(), "bundleHash", gomock.Len(3), []string{}).Return("newBundleHash", []string{}, nil).Times(1)
mockSnykCodeClient.EXPECT().ExtendBundle(gomock.Any(), "newBundleHash", gomock.Len(2), []string{}).Return("newerBundleHash", []string{}, nil).Times(1)
mockInstrumentor := mocks.NewMockInstrumentor(ctrl)
mockInstrumentor.EXPECT().StartSpan(gomock.Any(), gomock.Any()).Return(mockSpan).Times(2)
mockInstrumentor.EXPECT().Finish(gomock.Any()).Times(2)
Expand All @@ -329,7 +322,6 @@ func Test_Upload(t *testing.T) {
missingFiles = append(missingFiles, path)

_, err := bundleManager.Upload(context.Background(),
"testHost",
bundle.NewBundle(mockSnykCodeClient, mockInstrumentor, mockErrorReporter, &logger, "bundleHash", "testRequestId", "", bundleFileMap, []string{}, missingFiles),
bundleFileMap)
assert.Nil(t, err)
Expand All @@ -346,7 +338,7 @@ func createTempFileInDir(t *testing.T, name string, size int, temporaryDir strin
func Test_IsSupported_Extensions(t *testing.T) {
ctrl := gomock.NewController(t)
mockSnykCodeClient := mocks2.NewMockSnykCodeClient(ctrl)
mockSnykCodeClient.EXPECT().GetFilters(gomock.Any(), "testHost").Return(deepcode2.FiltersResponse{
mockSnykCodeClient.EXPECT().GetFilters(gomock.Any()).Return(deepcode2.FiltersResponse{
ConfigFiles: []string{},
Extensions: []string{".java"},
}, nil)
Expand All @@ -355,19 +347,19 @@ func Test_IsSupported_Extensions(t *testing.T) {
bundler := bundle.NewBundleManager(newLogger(t), mockSnykCodeClient, mockInstrumentor, mockErrorReporter)

t.Run("should return true for supported languages", func(t *testing.T) {
supported, _ := bundler.IsSupported(context.Background(), "testHost", "C:\\some\\path\\Test.java")
supported, _ := bundler.IsSupported(context.Background(), "C:\\some\\path\\Test.java")
assert.True(t, supported)
})

t.Run("should return false for unsupported languages", func(t *testing.T) {
supported, _ := bundler.IsSupported(context.Background(), "testHost", "C:\\some\\path\\Test.rs")
supported, _ := bundler.IsSupported(context.Background(), "C:\\some\\path\\Test.rs")
assert.False(t, supported)
})

t.Run("should cache supported extensions", func(t *testing.T) {
path := "C:\\some\\path\\Test.rs"
_, _ = bundler.IsSupported(context.Background(), "testHost", path)
_, _ = bundler.IsSupported(context.Background(), "testHost", path)
_, _ = bundler.IsSupported(context.Background(), path)
_, _ = bundler.IsSupported(context.Background(), path)
})
}

Expand All @@ -385,7 +377,7 @@ func Test_IsSupported_ConfigFiles(t *testing.T) {

ctrl := gomock.NewController(t)
mockSnykCodeClient := mocks2.NewMockSnykCodeClient(ctrl)
mockSnykCodeClient.EXPECT().GetFilters(gomock.Any(), "testHost").Return(deepcode2.FiltersResponse{
mockSnykCodeClient.EXPECT().GetFilters(gomock.Any()).Return(deepcode2.FiltersResponse{
ConfigFiles: configFilesFromFiltersEndpoint,
Extensions: []string{},
}, nil)
Expand All @@ -397,27 +389,27 @@ func Test_IsSupported_ConfigFiles(t *testing.T) {
t.Run("should return true for supported config files", func(t *testing.T) {
for _, file := range expectedConfigFiles {
path := filepath.Join(dir, file)
supported, _ := bundler.IsSupported(context.Background(), "testHost", path)
supported, _ := bundler.IsSupported(context.Background(), path)
assert.True(t, supported)
}
})
t.Run("should exclude .gitignore and .dcignore", func(t *testing.T) {
for _, file := range []string{".gitignore", ".dcignore"} {
path := filepath.Join(dir, file)
supported, _ := bundler.IsSupported(context.Background(), "testHost", path)
supported, _ := bundler.IsSupported(context.Background(), path)
assert.False(t, supported)
}
})
t.Run("should return false for unsupported config files", func(t *testing.T) {
path := "C:\\some\\path\\.unsupported"
supported, _ := bundler.IsSupported(context.Background(), "testHost", path)
supported, _ := bundler.IsSupported(context.Background(), path)
assert.False(t, supported)
})

t.Run("should cache supported extensions", func(t *testing.T) {
path := "C:\\some\\path\\Test.rs"
_, _ = bundler.IsSupported(context.Background(), "testHost", path)
_, _ = bundler.IsSupported(context.Background(), "testHost", path)
_, _ = bundler.IsSupported(context.Background(), path)
_, _ = bundler.IsSupported(context.Background(), path)
})
}

Expand Down
Loading