From bcf0295f58fa09f2fd3db17bd10885d9f6ffac2b Mon Sep 17 00:00:00 2001 From: Teodora Sandu Date: Fri, 5 Apr 2024 17:47:21 +0100 Subject: [PATCH] feat: use workspace client --- README.md | 45 ++++++++++------------------ config/config.go | 3 ++ config/mocks/config.go | 14 +++++++++ scan.go | 67 ++++++++++++++++++++++++++++++++++++++++-- scan_test.go | 6 ++-- 5 files changed, 102 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index cc909dd..b703f87 100644 --- a/README.md +++ b/README.md @@ -38,44 +38,31 @@ The HTTP client exposes a `DoCall` function. 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()) -``` - -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(logger, 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. The Code Scanner exposes a `UploadAndAnalyze` function, which can be used like this: ```go -codeScanner := codeclient.NewCodeScanner( +import ( + "net/http" + + "github.com/rs/zerolog" + code "github.com/snyk/code-client-go" +) + +logger := zerlog.NewLogger(...) +config := newConfigForMyApp() + +codeScanner := code.NewCodeScanner( + httpClient, + config, bundleManager, - testutil.NewTestInstrumentor(), - testutil.NewTestErrorReporter(), + codeInstrumentor, + codeErrorReporter, logger, ) -codeScanner.UploadAndAnalyze(context.Background(), "path/to/workspace", channelForWalkingFiles, changedFiles) +code.UploadAndAnalyze(context.Background(), "orgId", "requestId", "path/to/workspace", channelForWalkingFiles, changedFiles) ``` diff --git a/config/config.go b/config/config.go index ae4bb93..f670ce7 100644 --- a/config/config.go +++ b/config/config.go @@ -16,4 +16,7 @@ type Config interface { // SnykCodeApi returns the Snyk Code API URL configured to run against, which could be // the one used by the Local Code Engine. SnykCodeApi() string + + // SnykApi returns the Snyk REST API URL configured to run against, + SnykApi() string } diff --git a/config/mocks/config.go b/config/mocks/config.go index 428d2ca..c1132b4 100644 --- a/config/mocks/config.go +++ b/config/mocks/config.go @@ -61,6 +61,20 @@ func (mr *MockConfigMockRecorder) Organization() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Organization", reflect.TypeOf((*MockConfig)(nil).Organization)) } +// SnykApi mocks base method. +func (m *MockConfig) SnykApi() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SnykApi") + ret0, _ := ret[0].(string) + return ret0 +} + +// SnykApi indicates an expected call of SnykApi. +func (mr *MockConfigMockRecorder) SnykApi() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SnykApi", reflect.TypeOf((*MockConfig)(nil).SnykApi)) +} + // SnykCodeApi mocks base method. func (m *MockConfig) SnykCodeApi() string { m.ctrl.T.Helper() diff --git a/scan.go b/scan.go index c39e5f2..cd4ed47 100644 --- a/scan.go +++ b/scan.go @@ -19,7 +19,9 @@ package codeclient import ( "context" + "fmt" + "github.com/google/uuid" "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/rs/zerolog/log" @@ -29,12 +31,16 @@ import ( "github.com/snyk/code-client-go/internal/analysis" "github.com/snyk/code-client-go/internal/bundle" "github.com/snyk/code-client-go/internal/deepcode" + "github.com/snyk/code-client-go/internal/util" + workspaceClient "github.com/snyk/code-client-go/internal/workspace/2024-03-12" + externalRef3 "github.com/snyk/code-client-go/internal/workspace/2024-03-12/workspaces" "github.com/snyk/code-client-go/observability" "github.com/snyk/code-client-go/sarif" ) type codeScanner struct { bundleManager bundle.BundleManager + workspace *workspaceClient.ClientWithResponses errorReporter observability.ErrorReporter logger *zerolog.Logger } @@ -42,6 +48,7 @@ type codeScanner struct { type CodeScanner interface { UploadAndAnalyze( ctx context.Context, + orgId string, requestId string, path string, files <-chan string, @@ -56,14 +63,19 @@ func NewCodeScanner( instrumentor observability.Instrumentor, errorReporter observability.ErrorReporter, logger *zerolog.Logger, -) *codeScanner { +) (*codeScanner, error) { snykCode := deepcode.NewSnykCodeClient(logger, httpClient, instrumentor, errorReporter, config) bundleManager := bundle.NewBundleManager(logger, snykCode, instrumentor, errorReporter) + workspace, err := workspaceClient.NewClientWithResponses(config.SnykApi(), workspaceClient.WithHTTPClient(httpClient)) + if err != nil { + return nil, err + } return &codeScanner{ bundleManager: bundleManager, + workspace: workspace, errorReporter: errorReporter, logger: logger, - } + }, nil } // WithBundleManager creates a new Code Scanner from the current one and replaces the bundle manager. @@ -79,6 +91,7 @@ func (c *codeScanner) WithBundleManager(bundleManager bundle.BundleManager) *cod // UploadAndAnalyze returns a fake SARIF response for testing. Use target-service to run analysis on. func (c *codeScanner) UploadAndAnalyze( ctx context.Context, + orgId string, requestId string, path string, files <-chan string, @@ -128,6 +141,56 @@ func (c *codeScanner) UploadAndAnalyze( return nil, bundleHash, nil } + orgUUID := uuid.MustParse(orgId) + repositoryUri, err := util.GetRepositoryUrl(path) + if err != nil { + if ctx.Err() != nil { // Only handle errors that are not intentional cancellations + msg := "error retrieving Git info..." + c.errorReporter.CaptureError(errors.Wrap(err, msg), observability.ErrorReporterOptions{ErrorDiagnosticPath: path}) + return nil, bundleHash, err + } else { + log.Info().Msg("Canceling Code scan - Code scanner received cancellation signal") + return nil, bundleHash, nil + } + } + + workspaceResponse, err := c.workspace.CreateWorkspaceWithApplicationVndAPIPlusJSONBodyWithResponse(ctx, orgUUID, &workspaceClient.CreateWorkspaceParams{ + Version: "2024-03-12~experimental", + SnykRequestId: uuid.MustParse(requestId), + }, workspaceClient.CreateWorkspaceApplicationVndAPIPlusJSONRequestBody{ + Data: struct { + Attributes struct { + BundleId string `json:"bundle_id"` + RepositoryUri string `json:"repository_uri"` + WorkspaceType externalRef3.WorkspacePostRequestDataAttributesWorkspaceType `json:"workspace_type"` + } `json:"attributes"` + Type externalRef3.WorkspacePostRequestDataType `json:"type"` + }(struct { + Attributes struct { + BundleId string `json:"bundle_id"` + RepositoryUri string `json:"repository_uri"` + WorkspaceType externalRef3.WorkspacePostRequestDataAttributesWorkspaceType `json:"workspace_type"` + } + Type externalRef3.WorkspacePostRequestDataType + }{Attributes: struct { + BundleId string `json:"bundle_id"` + RepositoryUri string `json:"repository_uri"` + WorkspaceType externalRef3.WorkspacePostRequestDataAttributesWorkspaceType `json:"workspace_type"` + }(struct { + BundleId string + RepositoryUri string + WorkspaceType externalRef3.WorkspacePostRequestDataAttributesWorkspaceType + }{ + BundleId: b.GetBundleHash(), + RepositoryUri: repositoryUri, + WorkspaceType: "workspaceUri", + }), + Type: "workspace", + }), + }) + + fmt.Println(workspaceResponse.ApplicationvndApiJSON201.Data.Id) + response, err := analysis.RunAnalysis() if ctx.Err() != nil { c.logger.Info().Msg("Canceling Code scan - Code scanner received cancellation signal") diff --git a/scan_test.go b/scan_test.go index c387b46..03dd1d7 100644 --- a/scan_test.go +++ b/scan_test.go @@ -67,7 +67,8 @@ func Test_UploadAndAnalyze(t *testing.T) { mockBundleManager.EXPECT().Create(gomock.Any(), "testRequestId", baseDir, gomock.Any(), map[string]bool{}).Return(mockBundle, nil) mockBundleManager.EXPECT().Upload(gomock.Any(), "testRequestId", mockBundle, files).Return(mockBundle, nil) - codeScanner := codeclient.NewCodeScanner(mockHTTPClient, mockConfig, mockInstrumentor, mockErrorReporter, &logger) + codeScanner, err := codeclient.NewCodeScanner(mockHTTPClient, mockConfig, mockInstrumentor, mockErrorReporter, &logger) + require.NoError(t, err) response, bundleHash, err := codeScanner.WithBundleManager(mockBundleManager).UploadAndAnalyze(context.Background(), "testRequestId", baseDir, docs, map[string]bool{}) require.NoError(t, err) @@ -83,7 +84,8 @@ func Test_UploadAndAnalyze(t *testing.T) { mockBundleManager.EXPECT().Create(gomock.Any(), "testRequestId", baseDir, gomock.Any(), map[string]bool{}).Return(mockBundle, nil) mockBundleManager.EXPECT().Upload(gomock.Any(), "testRequestId", mockBundle, files).Return(mockBundle, nil) - codeScanner := codeclient.NewCodeScanner(mockHTTPClient, mockConfig, mockInstrumentor, mockErrorReporter, &logger) + codeScanner, err := codeclient.NewCodeScanner(mockHTTPClient, mockConfig, mockInstrumentor, mockErrorReporter, &logger) + require.NoError(t, err) response, bundleHash, err := codeScanner.WithBundleManager(mockBundleManager).UploadAndAnalyze(context.Background(), "testRequestId", baseDir, docs, map[string]bool{}) require.NoError(t, err)