Skip to content

Commit 0b7d5f7

Browse files
feat: progress tracker [IDE-222] (#49)
1 parent 2b7057d commit 0b7d5f7

File tree

18 files changed

+470
-95
lines changed

18 files changed

+470
-95
lines changed

README.md

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,39 @@ httpClient := codeClientHTTP.NewHTTPClient(
4646

4747
The HTTP client exposes a `Do` function.
4848

49+
### Target
50+
51+
Use the target to record the target of a scan, which can be either a folder enhanced with repository metadata
52+
or a repository.
53+
54+
```go
55+
import (
56+
codeClientScan "github.com/snyk/code-client-go/scan"
57+
)
58+
59+
target, _ := codeClientScan.NewRepositoryTarget(path)
60+
61+
target, _ := codeClientScan.NewRepositoryTarget(path, codeClientScan.WithRepositoryUrl("https://github.com/snyk/code-client-go.git"))
62+
```
63+
### Tracker Factory
64+
65+
Use the tracker factory to generate a tracker used to update the consumer of the client with frequent progress updates.
66+
67+
The tracker either exposes an interface with two `Begin` and `End` functions or an implementation that doesn't do anything.
68+
69+
```go
70+
import (
71+
codeClientScan "github.com/snyk/code-client-go/scan"
72+
)
73+
74+
trackerFactory := codeClientScan.NewNoopTrackerFactory()
75+
76+
tracker := trackerFactory.GenerateTracker()
77+
tracker.Begin()
78+
...
79+
tracker.End()
80+
```
81+
4982
### Configuration
5083

5184
Implement the `config.Config` interface to configure the Snyk Code API client from applications.
@@ -57,15 +90,20 @@ Use the Code Scanner to trigger a scan for a Snyk Code workspace using the Bundl
5790
The Code Scanner exposes a `UploadAndAnalyze` function, which can be used like this:
5891

5992
```go
93+
import (
94+
codeClient "github.com/snyk/code-client-go"
95+
)
96+
6097
config := newConfigForMyApp()
61-
codeScanner := code.NewCodeScanner(
98+
codeScanner := codeClient.NewCodeScanner(
6299
httpClient,
63100
config,
101+
codeClient.WithTrackerFactory(trackerFactory),
64102
codeClientHTTP.WithLogger(logger),
65103
codeClientHTTP.WithInstrumentor(instrumentor),
66104
codeClientHTTP.WithErrorReporter(errorReporter),
67105
)
68-
code.UploadAndAnalyze(context.Background(), requestId, "path/to/workspace", channelForWalkingFiles, changedFiles)
106+
codeScanner.UploadAndAnalyze(context.Background(), requestId, target, channelForWalkingFiles, changedFiles)
69107
```
70108

71109

internal/analysis/analysis.go

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,15 @@ import (
4747
//go:generate mockgen -destination=mocks/analysis.go -source=analysis.go -package mocks
4848
type AnalysisOrchestrator interface {
4949
CreateWorkspace(ctx context.Context, orgId string, requestId string, path scan.Target, bundleHash string) (string, error)
50-
RunAnalysis(ctx context.Context, orgId string, workspaceId string) (*sarif.SarifResponse, error)
50+
RunAnalysis(ctx context.Context, orgId string, rootPath string, workspaceId string) (*sarif.SarifResponse, error)
5151
}
5252

5353
type analysisOrchestrator struct {
5454
httpClient codeClientHTTP.HTTPClient
5555
instrumentor observability.Instrumentor
5656
errorReporter observability.ErrorReporter
5757
logger *zerolog.Logger
58+
trackerFactory scan.TrackerFactory
5859
config config.Config
5960
timeoutInSeconds time.Duration
6061
}
@@ -73,13 +74,15 @@ func NewAnalysisOrchestrator(
7374
httpClient codeClientHTTP.HTTPClient,
7475
instrumentor observability.Instrumentor,
7576
errorReporter observability.ErrorReporter,
77+
trackerFactory scan.TrackerFactory,
7678
options ...OptionFunc,
7779
) AnalysisOrchestrator {
7880
a := &analysisOrchestrator{
7981
httpClient: httpClient,
8082
instrumentor: instrumentor,
8183
errorReporter: errorReporter,
8284
logger: logger,
85+
trackerFactory: trackerFactory,
8386
config: config,
8487
timeoutInSeconds: 120 * time.Second,
8588
}
@@ -98,6 +101,10 @@ func (a *analysisOrchestrator) CreateWorkspace(ctx context.Context, orgId string
98101
span := a.instrumentor.StartSpan(ctx, method)
99102
defer a.instrumentor.Finish(span)
100103

104+
tracker := a.trackerFactory.GenerateTracker()
105+
tracker.Begin("Creating file bundle workspace", "")
106+
defer tracker.End("")
107+
101108
orgUUID := uuid.MustParse(orgId)
102109

103110
if target == nil {
@@ -181,23 +188,44 @@ func (a *analysisOrchestrator) CreateWorkspace(ctx context.Context, orgId string
181188
return workspaceId, nil
182189
}
183190

184-
func (a *analysisOrchestrator) RunAnalysis(ctx context.Context, orgId string, workspaceId string) (*sarif.SarifResponse, error) {
191+
func (a *analysisOrchestrator) RunAnalysis(ctx context.Context, orgId string, rootPath string, workspaceId string) (*sarif.SarifResponse, error) {
185192
method := "analysis.RunAnalysis"
186193
logger := a.logger.With().Str("method", method).Logger()
187194
logger.Debug().Msg("API: Creating the scan")
195+
196+
tracker := a.trackerFactory.GenerateTracker()
197+
tracker.Begin("Snyk Code analysis for "+rootPath, "Retrieving results...")
198+
188199
org := uuid.MustParse(orgId)
189200

190201
host := a.host(false)
191202
a.logger.Debug().Str("host", host).Str("workspaceId", workspaceId).Msg("starting scan")
192203

193204
client, err := orchestrationClient.NewClientWithResponses(host, orchestrationClient.WithHTTPClient(a.httpClient))
194-
195205
if err != nil {
206+
tracker.End(fmt.Sprintf("Analysis failed: %v", err))
196207
return nil, fmt.Errorf("failed to create orchestrationClient: %w", err)
197208
}
198209

210+
scanJobId, err := a.triggerScan(ctx, client, org, workspaceId)
211+
if err != nil {
212+
tracker.End(fmt.Sprintf("Analysis failed: %v", err))
213+
return nil, err
214+
}
215+
216+
response, err := a.pollScanForFindings(ctx, client, org, *scanJobId)
217+
if err != nil {
218+
tracker.End(fmt.Sprintf("Analysis failed: %v", err))
219+
return nil, err
220+
}
221+
222+
tracker.End("Analysis complete.")
223+
return response, nil
224+
}
225+
226+
func (a *analysisOrchestrator) triggerScan(ctx context.Context, client *orchestrationClient.ClientWithResponses, org uuid.UUID, workspaceId string) (*openapi_types.UUID, error) {
199227
flow := scans.Flow{}
200-
err = flow.UnmarshalJSON([]byte(`{"name": "cli_test"}`))
228+
err := flow.UnmarshalJSON([]byte(`{"name": "cli_test"}`))
201229
if err != nil {
202230
return nil, fmt.Errorf("failed to create scan request: %w", err)
203231
}
@@ -242,7 +270,7 @@ func (a *analysisOrchestrator) RunAnalysis(ctx context.Context, orgId string, wo
242270
switch createScanResponse.StatusCode() {
243271
case 201:
244272
scanJobId = createScanResponse.ApplicationvndApiJSON201.Data.Id
245-
a.logger.Debug().Str("host", host).Str("workspaceId", workspaceId).Msg("starting scan")
273+
a.logger.Debug().Str("workspaceId", workspaceId).Msg("starting scan")
246274
case 400:
247275
msg = createScanResponse.ApplicationvndApiJSON400.Errors[0].Detail
248276
case 401:
@@ -260,12 +288,7 @@ func (a *analysisOrchestrator) RunAnalysis(ctx context.Context, orgId string, wo
260288
return nil, errors.New(msg)
261289
}
262290

263-
response, err := a.pollScanForFindings(ctx, client, org, scanJobId)
264-
if err != nil {
265-
return nil, err
266-
}
267-
268-
return response, nil
291+
return &scanJobId, nil
269292
}
270293

271294
func (a *analysisOrchestrator) pollScanForFindings(ctx context.Context, client *orchestrationClient.ClientWithResponses, org uuid.UUID, scanJobId openapi_types.UUID) (*sarif.SarifResponse, error) {
@@ -279,7 +302,7 @@ func (a *analysisOrchestrator) pollScanForFindings(ctx context.Context, client *
279302
for {
280303
select {
281304
case <-timeoutTimer.C:
282-
msg := "timeout requesting the ScanJobResult"
305+
msg := "Snyk Code analysis timed out"
283306
logger.Error().Str("scanJobId", scanJobId.String()).Msg(msg)
284307
return nil, errors.New(msg)
285308
case <-pollingTicker.C:

0 commit comments

Comments
 (0)