Skip to content

Commit

Permalink
feat: support event processing status endpoint (#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
nscuro authored Oct 27, 2024
1 parent 388e284 commit 096b70c
Show file tree
Hide file tree
Showing 7 changed files with 331 additions and 45 deletions.
100 changes: 71 additions & 29 deletions about_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,89 @@ package dtrack

import (
"context"
"net/http"
"github.com/google/uuid"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
"log"
"testing"

"github.com/jarcoal/httpmock"
"github.com/stretchr/testify/require"
)

func TestAboutService_Get(t *testing.T) {
client, err := NewClient("http://localhost")
require.NoError(t, err)

httpmock.ActivateNonDefault(client.httpClient)
defer httpmock.DeactivateAndReset()

httpmock.RegisterResponder(http.MethodGet, "http://localhost/api/version",
httpmock.NewStringResponder(http.StatusOK, `{
"timestamp": "2020-09-29T22:02:23Z",
"version": "4.0.0-SNAPSHOT",
"uuid": "c35ce882-398d-46ed-8c36-6148bf73f941",
"systemUuid": "f2eee6f6-a161-418a-baf5-b57a2e30de82",
"application": "Dependency-Track",
"framework": {
"timestamp": "2020-07-20T15:56:44Z",
"version": "1.8.0-SNAPSHOT",
"uuid": "beee8786-ca9c-473a-b7a5-efcc95e8c469",
"name": "Alpine"
}
}`))
_, client := setUpContainer(t)

about, err := client.About.Get(context.TODO())
require.NoError(t, err)
require.NotNil(t, about)

require.Equal(t, "2020-09-29T22:02:23Z", about.Timestamp)
require.Equal(t, "4.0.0-SNAPSHOT", about.Version)
require.Equal(t, "c35ce882-398d-46ed-8c36-6148bf73f941", about.UUID.String())
require.Equal(t, "f2eee6f6-a161-418a-baf5-b57a2e30de82", about.SystemUUID.String())
require.NotEmpty(t, about.Timestamp)
require.NotEmpty(t, about.Version)
require.NotEqual(t, uuid.Nil, about.UUID)
require.NotEqual(t, uuid.Nil, about.SystemUUID)
require.Equal(t, "Dependency-Track", about.Application)

require.Equal(t, "2020-07-20T15:56:44Z", about.Framework.Timestamp)
require.Equal(t, "1.8.0-SNAPSHOT", about.Framework.Version)
require.Equal(t, "beee8786-ca9c-473a-b7a5-efcc95e8c469", about.Framework.UUID.String())
require.NotEmpty(t, about.Framework.Timestamp)
require.NotEmpty(t, about.Framework.Version)
require.NotEqual(t, uuid.Nil, about.Framework.UUID)
require.Equal(t, "Alpine", about.Framework.Name)
}

func setUpContainer(t *testing.T) (testcontainers.Container, *Client) {
ctx := context.Background()

container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: testcontainers.ContainerRequest{
Image: "dependencytrack/apiserver:latest",
Env: map[string]string{
"JAVA_OPTIONS": "-Xmx1g",
"SYSTEM_REQUIREMENT_CHECK_ENABLED": "false",
},
ExposedPorts: []string{"8080/tcp"},
WaitingFor: wait.ForLog("Dependency-Track is ready"),
},
Started: true,
})

t.Cleanup(func() {
err = container.Terminate(ctx)
if err != nil {
log.Fatalf("failed to terminate container: %v", err)
}
})
require.NoError(t, err)

apiURL, err := container.Endpoint(ctx, "http")
require.NoError(t, err)

client, err := NewClient(apiURL)
require.NoError(t, err)

err = client.User.ForceChangePassword(ctx, "admin", "admin", "test")
require.NoError(t, err)

bearerToken, err := client.User.Login(ctx, "admin", "test")
require.NoError(t, err)

client, err = NewClient(apiURL, WithBearerToken(bearerToken))
require.NoError(t, err)

// TODO: Pass desired permissions as parameter to setUpContainer
team, err := client.Team.Create(ctx, Team{
Name: "test",
Permissions: []Permission{
{
Name: "VIEW_PORTFOLIO",
},
},
})
require.NoError(t, err)

apiKey, err := client.Team.GenerateAPIKey(ctx, team.UUID)
require.NoError(t, err)

client, err = NewClient(apiURL, WithAPIKey(apiKey))
require.NoError(t, err)

return container, client
}
10 changes: 8 additions & 2 deletions bom.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ package dtrack
import (
"context"
"fmt"
"github.com/google/uuid"
"net/http"
"net/url"

"github.com/google/uuid"
)

type BOMService struct {
Expand Down Expand Up @@ -144,7 +143,14 @@ type bomProcessingResponse struct {
Processing bool `json:"processing"`
}

// IsBeingProcessed checks whether the BOM associated with a given token is still being processed.
//
// Deprecated: for server versions 4.11.0 and above, EventService.IsBeingProcessed should be used.
func (bs BOMService) IsBeingProcessed(ctx context.Context, token BOMUploadToken) (bool, error) {
if bs.client.isServerVersionAtLeast("4.11.0") {
return bs.client.Event.IsBeingProcessed(ctx, EventToken(token))
}

req, err := bs.client.newRequest(ctx, http.MethodGet, fmt.Sprintf("/api/v1/bom/token/%s", token))
if err != nil {
return false, err
Expand Down
4 changes: 2 additions & 2 deletions bom_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func Example_uploadBOM() {
for {
select {
case <-ticker.C:
processing, err := client.BOM.IsBeingProcessed(context.TODO(), uploadToken)
processing, err := client.Event.IsBeingProcessed(context.TODO(), dtrack.EventToken(uploadToken))
if err != nil {
errChan <- err
return
Expand All @@ -64,7 +64,7 @@ func Example_uploadBOM() {
select {
case <-doneChan:
fmt.Println("bom processing completed")
case <-errChan:
case err = <-errChan:
fmt.Printf("failed to wait for bom processing: %v\n", err)
}
}
25 changes: 25 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"encoding/json"
"errors"
"fmt"
"golang.org/x/mod/semver"
"io"
"log"
"mime/multipart"
Expand All @@ -32,12 +33,14 @@ type Client struct {
baseURL *url.URL
userAgent string
debug bool
about About

About AboutService
Analysis AnalysisService
BOM BOMService
Component ComponentService
Finding FindingService
Event EventService
License LicenseService
Metrics MetricsService
OIDC OIDCService
Expand Down Expand Up @@ -85,6 +88,7 @@ func NewClient(baseURL string, options ...ClientOption) (*Client, error) {
client.BOM = BOMService{client: &client}
client.Component = ComponentService{client: &client}
client.Finding = FindingService{client: &client}
client.Event = EventService{client: &client}
client.License = LicenseService{client: &client}
client.Metrics = MetricsService{client: &client}
client.OIDC = OIDCService{client: &client}
Expand All @@ -101,6 +105,11 @@ func NewClient(baseURL string, options ...ClientOption) (*Client, error) {
client.ViolationAnalysis = ViolationAnalysisService{client: &client}
client.Vulnerability = VulnerabilityService{client: &client}

client.about, err = client.About.Get(context.Background())
if err != nil {
return nil, fmt.Errorf("failed to fetch version information: %w", err)
}

return &client, nil
}

Expand All @@ -110,6 +119,22 @@ func (c Client) BaseURL() *url.URL {
return &u
}

func (c Client) isServerVersionAtLeast(targetVersion string) bool {
// semver requires versions to be prefixed with "v",
// and doesn't support "-SNAPSHOT" suffixes.
targetVersionNormalized := fmt.Sprintf("v%s", targetVersion)
actualVersionNormalized := fmt.Sprintf("v%s", strings.TrimSuffix(c.about.Version, "-SNAPSHOT"))
return semver.Compare(targetVersionNormalized, actualVersionNormalized) >= 0
}

func (c Client) assertServerVersionAtLeast(targetVersion string) error {
if c.isServerVersionAtLeast(targetVersion) {
return fmt.Errorf("server version must be at least %s, but is %s", targetVersion, c.about.Version)
}

return nil
}

func (c Client) newRequest(ctx context.Context, method, path string, options ...requestOption) (*http.Request, error) {
u, err := c.baseURL.Parse(path)
if err != nil {
Expand Down
38 changes: 38 additions & 0 deletions event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package dtrack

import (
"context"
"fmt"
"net/http"
)

type EventService struct {
client *Client
}

type EventToken string

type eventProcessingResponse struct {
Processing bool `json:"processing"`
}

// IsBeingProcessed checks whether the event associated with a given token is still being processed.
func (es EventService) IsBeingProcessed(ctx context.Context, token EventToken) (bool, error) {
err := es.client.assertServerVersionAtLeast("4.11.0")
if err != nil {
return false, err
}

req, err := es.client.newRequest(ctx, http.MethodGet, fmt.Sprintf("/api/v1/event/token/%s", token))
if err != nil {
return false, err
}

var processingResponse eventProcessingResponse
_, err = es.client.doRequest(req, &processingResponse)
if err != nil {
return false, err
}

return processingResponse.Processing, nil
}
35 changes: 33 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,43 @@ go 1.19
require (
github.com/google/go-cmp v0.5.9
github.com/google/uuid v1.3.0
github.com/jarcoal/httpmock v1.3.0
github.com/stretchr/testify v1.8.2
github.com/stretchr/testify v1.8.4
github.com/testcontainers/testcontainers-go v0.22.0
golang.org/x/mod v0.20.0
)

require (
dario.cat/mergo v1.0.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
github.com/containerd/containerd v1.7.3 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/docker v24.0.5+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/klauspost/compress v1.16.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/moby/patternmatcher v0.5.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc4 // indirect
github.com/opencontainers/runc v1.1.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/tools v0.13.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
google.golang.org/grpc v1.57.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit 096b70c

Please sign in to comment.