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
8 changes: 8 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ git --version
go version
```

Also make sure to install the tools required for development by running `make generate`.

## Setting up

Clone this repository with git.
Expand All @@ -41,6 +43,12 @@ To run the tests run:
make test
```

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

If writing `pact` or integration tests, use the test implementations in [./internal/util/testutil](./internal/util/testutil).

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

## Code ownership

For current ownership assignments, see: [CODEOWNERS](./.github/CODEOWNERS).
Expand Down
66 changes: 66 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,69 @@
# 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).

## Installation

```shell script
$ go get github.com/snyk/code-client-go
```

## Usage

### HTTP Client

Use the HTTP client to make HTTP requests with configured retriable codes and authorisation headers for Snyk Rest APIs.

```go
engine := workflow.NewDefaultWorkFlowEngine()
httpClient := http.NewHTTPClient(engine, engine.GetNetworkAccess().GetHttpClient, codeInstrumentor, codeErrorReporter)
```

The HTTP client exposes a `DoCall` function.


### Snyk Code Client

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


```go
engine := workflow.NewDefaultWorkFlowEngine()
snykCode := deepcode.NewSnykCodeClient(engine, httpClient, testutil.NewTestInstrumentor())
```

The Snyk Code Client exposes the following functions:
- `GetFilters`
- `CreateBundle`
- `ExtendBundle`

### Bundle Manager

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())
```

The Bundle Manager exposes the following functions:
- `Create`
- `Upload`

### Code Scanner

Use the Code Scanner to trigger a scan for a Snyk Code workspace using the Bundle Manager created above:

```go
codeScanner := codeclient.NewCodeScanner(
bundleManager,
testutil.NewTestInstrumentor(),
testutil.NewTestCodeInstrumentor(),
testutils.NewTestAnalytics(),
)
```

The Code Scanner exposes a `UploadAndAnalyze` function.

### Observability

Under [./observability](./observability) we have defined some observability interfaces which allows consumers of the library to inject their own observability implementations as long as they follow the defined interfaces.
22 changes: 17 additions & 5 deletions bundle/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,13 @@ package bundle

import (
"context"

"github.com/rs/zerolog/log"
"github.com/rs/zerolog"

"github.com/snyk/code-client-go/deepcode"
"github.com/snyk/code-client-go/observability"
)

//go:generate mockgen -destination=mocks/deepCodeBundle.go -source=deepCodeBundle.go -package mocks
//go:generate mockgen -destination=mocks/bundle.go -source=bundle.go -package mocks
type Bundle interface {
UploadBatch(ctx context.Context, host string, batch *Batch) error
GetBundleHash() string
Expand All @@ -39,6 +38,7 @@ type deepCodeBundle struct {
SnykCode deepcode.SnykCodeClient
instrumentor observability.Instrumentor
errorReporter observability.ErrorReporter
logger *zerolog.Logger
requestId string
rootPath string
files map[string]deepcode.BundleFile
Expand All @@ -48,11 +48,23 @@ type deepCodeBundle struct {
limitToFiles []string
}

func NewBundle(snykCode deepcode.SnykCodeClient, instrumentor observability.Instrumentor, errorReporter observability.ErrorReporter, bundleHash string, requestId string, rootPath string, files map[string]deepcode.BundleFile, limitToFiles []string, missingFiles []string) *deepCodeBundle {
func NewBundle(
snykCode deepcode.SnykCodeClient,
instrumentor observability.Instrumentor,
errorReporter observability.ErrorReporter,
logger *zerolog.Logger,
bundleHash string,
requestId string,
rootPath string,
files map[string]deepcode.BundleFile,
limitToFiles []string,
missingFiles []string,
) *deepCodeBundle {
return &deepCodeBundle{
SnykCode: snykCode,
instrumentor: instrumentor,
errorReporter: errorReporter,
logger: logger,
bundleHash: bundleHash,
requestId: requestId,
rootPath: rootPath,
Expand Down Expand Up @@ -96,7 +108,7 @@ func (b *deepCodeBundle) extendBundle(ctx context.Context, host string, uploadBa
var err error
if uploadBatch.hasContent() {
b.bundleHash, b.missingFiles, err = b.SnykCode.ExtendBundle(ctx, host, b.bundleHash, uploadBatch.documents, []string{})
log.Debug().Str("requestId", b.requestId).Interface("MissingFiles", b.missingFiles).Msg("extended deepCodeBundle on backend")
b.logger.Debug().Str("requestId", b.requestId).Interface("MissingFiles", b.missingFiles).Msg("extended deepCodeBundle on backend")
}

return err
Expand Down
22 changes: 15 additions & 7 deletions bundle/bundle_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ package bundle

import (
"context"
"github.com/rs/zerolog"
"github.com/snyk/go-application-framework/pkg/workflow"
"os"
"path/filepath"

"github.com/puzpuzpuz/xsync"
"github.com/rs/zerolog/log"

"github.com/snyk/code-client-go/deepcode"
"github.com/snyk/code-client-go/internal/util"
"github.com/snyk/code-client-go/observability"
Expand All @@ -34,6 +34,7 @@ type bundleManager struct {
SnykCode deepcode.SnykCodeClient
instrumentor observability.Instrumentor
errorReporter observability.ErrorReporter
logger *zerolog.Logger
supportedExtensions *xsync.MapOf[string, bool]
supportedConfigFiles *xsync.MapOf[string, bool]
}
Expand All @@ -56,11 +57,17 @@ type BundleManager interface {
) (Bundle, error)
}

func NewBundleManager(SnykCode deepcode.SnykCodeClient, instrumentor observability.Instrumentor, errorReporter observability.ErrorReporter) *bundleManager {
func NewBundleManager(
engine workflow.Engine,
SnykCode deepcode.SnykCodeClient,
instrumentor observability.Instrumentor,
errorReporter observability.ErrorReporter,
) *bundleManager {
return &bundleManager{
SnykCode: SnykCode,
instrumentor: instrumentor,
errorReporter: errorReporter,
logger: engine.GetLogger(),
supportedExtensions: xsync.NewMapOf[bool](),
supportedConfigFiles: xsync.NewMapOf[bool](),
}
Expand Down Expand Up @@ -96,7 +103,7 @@ func (b *bundleManager) Create(ctx context.Context,
var fileContent []byte
fileContent, err = os.ReadFile(absoluteFilePath)
if err != nil {
log.Error().Err(err).Str("filePath", absoluteFilePath).Msg("could not load content of file")
b.logger.Error().Err(err).Str("filePath", absoluteFilePath).Msg("could not load content of file")
continue
}

Expand Down Expand Up @@ -133,6 +140,7 @@ func (b *bundleManager) Create(ctx context.Context,
b.SnykCode,
b.instrumentor,
b.errorReporter,
b.logger,
bundleHash,
requestId,
rootPath,
Expand Down Expand Up @@ -193,10 +201,10 @@ func (b *bundleManager) groupInBatches(
file := files[filePath]
var fileContent = []byte(file.Content)
if batch.canFitFile(filePath, fileContent) {
log.Trace().Str("path", filePath).Int("size", len(fileContent)).Msgf("added to deepCodeBundle #%v", len(batches))
b.logger.Trace().Str("path", filePath).Int("size", len(fileContent)).Msgf("added to deepCodeBundle #%v", len(batches))
batch.documents[filePath] = file
} else {
log.Trace().Str("path", filePath).Int("size", len(fileContent)).Msgf("created new deepCodeBundle - %v bundles in this upload so far", len(batches))
b.logger.Trace().Str("path", filePath).Int("size", len(fileContent)).Msgf("created new deepCodeBundle - %v bundles in this upload so far", len(batches))
newUploadBatch := NewBatch(map[string]deepcode.BundleFile{})
newUploadBatch.documents[filePath] = file
batches = append(batches, newUploadBatch)
Expand All @@ -210,7 +218,7 @@ func (b *bundleManager) IsSupported(ctx context.Context, host string, file strin
if b.supportedExtensions.Size() == 0 && b.supportedConfigFiles.Size() == 0 {
filters, err := b.SnykCode.GetFilters(ctx, host)
if err != nil {
log.Error().Err(err).Msg("could not get filters")
b.logger.Error().Err(err).Msg("could not get filters")
return false, err
}

Expand Down
34 changes: 19 additions & 15 deletions bundle/bundle_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,22 @@ package bundle_test
import (
"bytes"
"context"
deepcode2 "github.com/snyk/code-client-go/deepcode"
mocks2 "github.com/snyk/code-client-go/deepcode/mocks"
"github.com/rs/zerolog"
"os"
"path/filepath"
"strings"
"testing"

"github.com/golang/mock/gomock"
"github.com/snyk/code-client-go/observability/mocks"
"github.com/snyk/go-application-framework/pkg/workflow"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/snyk/code-client-go/bundle"
deepcode2 "github.com/snyk/code-client-go/deepcode"
mocks2 "github.com/snyk/code-client-go/deepcode/mocks"
"github.com/snyk/code-client-go/internal/util"
"github.com/snyk/code-client-go/observability/mocks"
)

func Test_Create(t *testing.T) {
Expand Down Expand Up @@ -60,7 +62,7 @@ func Test_Create(t *testing.T) {
err := os.WriteFile(file, []byte(data), 0600)
require.NoError(t, err)

var bundleManager = bundle.NewBundleManager(mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
var bundleManager = bundle.NewBundleManager(workflow.NewDefaultWorkFlowEngine(), mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
bundle, err := bundleManager.Create(context.Background(),
"testHost",
"testRequestId",
Expand Down Expand Up @@ -93,7 +95,7 @@ func Test_Create(t *testing.T) {
err := os.WriteFile(file, []byte(data), 0600)
require.NoError(t, err)

var bundleManager = bundle.NewBundleManager(mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
var bundleManager = bundle.NewBundleManager(workflow.NewDefaultWorkFlowEngine(), mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
bundle, err := bundleManager.Create(context.Background(),
"testHost",
"testRequestId",
Expand Down Expand Up @@ -131,7 +133,7 @@ func Test_Create(t *testing.T) {
)
require.NoError(t, err)

var bundleManager = bundle.NewBundleManager(mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
var bundleManager = bundle.NewBundleManager(workflow.NewDefaultWorkFlowEngine(), mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
bundle, err := bundleManager.Create(context.Background(),
"testHost",
"testRequestId",
Expand Down Expand Up @@ -168,7 +170,7 @@ func Test_Create(t *testing.T) {
},
)
require.NoError(t, err)
var bundleManager = bundle.NewBundleManager(mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
var bundleManager = bundle.NewBundleManager(workflow.NewDefaultWorkFlowEngine(), mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
bundle, err := bundleManager.Create(context.Background(),
"testHost",
"testRequestId",
Expand Down Expand Up @@ -202,7 +204,7 @@ func Test_Create(t *testing.T) {
err := os.WriteFile(file, []byte("some content so the file won't be skipped"), 0600)
assert.Nil(t, err)

var bundleManager = bundle.NewBundleManager(mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
var bundleManager = bundle.NewBundleManager(workflow.NewDefaultWorkFlowEngine(), mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
bundle, err := bundleManager.Create(context.Background(),
"testHost",
"testRequestId",
Expand Down Expand Up @@ -251,7 +253,7 @@ func Test_Create(t *testing.T) {
require.NoError(t, err)
}

var bundleManager = bundle.NewBundleManager(mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
var bundleManager = bundle.NewBundleManager(workflow.NewDefaultWorkFlowEngine(), mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
bundle, err := bundleManager.Create(context.Background(),
"testHost",
"testRequestId",
Expand All @@ -271,6 +273,8 @@ func Test_Upload(t *testing.T) {
_ = os.RemoveAll(temporaryDir)
})

logger := zerolog.Nop()

t.Run("adds files to deepCodeBundle", func(t *testing.T) {
ctrl := gomock.NewController(t)
mockSpan := mocks.NewMockSpan(ctrl)
Expand All @@ -282,14 +286,14 @@ func Test_Upload(t *testing.T) {
mockInstrumentor.EXPECT().Finish(gomock.Any()).Times(2)
mockErrorReporter := mocks.NewMockErrorReporter(ctrl)

var bundleManager = bundle.NewBundleManager(mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
var bundleManager = bundle.NewBundleManager(workflow.NewDefaultWorkFlowEngine(), mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
documentURI, bundleFile := createTempFileInDir(t, "bundleDoc.java", 10, temporaryDir)
bundleFileMap := map[string]deepcode2.BundleFile{}
bundleFileMap[documentURI] = bundleFile

_, err := bundleManager.Upload(context.Background(),
"testHost",
bundle.NewBundle(mockSnykCodeClient, mockInstrumentor, mockErrorReporter, "bundleHash", "testRequestId", "", bundleFileMap, []string{}, []string{documentURI}),
bundle.NewBundle(mockSnykCodeClient, mockInstrumentor, mockErrorReporter, &logger, "bundleHash", "testRequestId", "", bundleFileMap, []string{}, []string{documentURI}),
bundleFileMap)
assert.NoError(t, err)
})
Expand All @@ -305,7 +309,7 @@ func Test_Upload(t *testing.T) {
mockInstrumentor.EXPECT().StartSpan(gomock.Any(), gomock.Any()).Return(mockSpan).Times(2)
mockInstrumentor.EXPECT().Finish(gomock.Any()).Times(2)
mockErrorReporter := mocks.NewMockErrorReporter(ctrl)
var bundleManager = bundle.NewBundleManager(mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
var bundleManager = bundle.NewBundleManager(workflow.NewDefaultWorkFlowEngine(), mockSnykCodeClient, mockInstrumentor, mockErrorReporter)

bundleFileMap := map[string]deepcode2.BundleFile{}
var missingFiles []string
Expand All @@ -327,7 +331,7 @@ func Test_Upload(t *testing.T) {

_, err := bundleManager.Upload(context.Background(),
"testHost",
bundle.NewBundle(mockSnykCodeClient, mockInstrumentor, mockErrorReporter, "bundleHash", "testRequestId", "", bundleFileMap, []string{}, missingFiles),
bundle.NewBundle(mockSnykCodeClient, mockInstrumentor, mockErrorReporter, &logger, "bundleHash", "testRequestId", "", bundleFileMap, []string{}, missingFiles),
bundleFileMap)
assert.Nil(t, err)
})
Expand All @@ -349,7 +353,7 @@ func Test_IsSupported_Extensions(t *testing.T) {
}, nil)
mockInstrumentor := mocks.NewMockInstrumentor(ctrl)
mockErrorReporter := mocks.NewMockErrorReporter(ctrl)
bundler := bundle.NewBundleManager(mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
bundler := bundle.NewBundleManager(workflow.NewDefaultWorkFlowEngine(), 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")
Expand Down Expand Up @@ -388,7 +392,7 @@ func Test_IsSupported_ConfigFiles(t *testing.T) {
}, nil)
mockInstrumentor := mocks.NewMockInstrumentor(ctrl)
mockErrorReporter := mocks.NewMockErrorReporter(ctrl)
bundler := bundle.NewBundleManager(mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
bundler := bundle.NewBundleManager(workflow.NewDefaultWorkFlowEngine(), mockSnykCodeClient, mockInstrumentor, mockErrorReporter)
dir, _ := os.Getwd()

t.Run("should return true for supported config files", func(t *testing.T) {
Expand Down
Loading