Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support event processing status endpoint #29

Merged
merged 2 commits into from
Oct 27, 2024
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
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
Loading