Skip to content

Commit

Permalink
🌱 Add ProjectPackageClient interface and deps.dev default client (#3954)
Browse files Browse the repository at this point in the history
* Add ProjectPackageClient interface and deps.dev default client

Signed-off-by: Raghav Kaul <raghavkaul+github@google.com>

* v4 -> v5

Signed-off-by: Raghav Kaul <raghavkaul+github@google.com>

* Move to internal

Signed-off-by: Raghav Kaul <raghavkaul+github@google.com>

* move internal to higher-level w/ shared root

Signed-off-by: Raghav Kaul <raghavkaul+github@google.com>

* update

Signed-off-by: Raghav Kaul <raghavkaul+github@google.com>

---------

Signed-off-by: Raghav Kaul <raghavkaul+github@google.com>
  • Loading branch information
raghavkaul authored May 8, 2024
1 parent c92efe9 commit f842292
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 0 deletions.
55 changes: 55 additions & 0 deletions e2e/depsdev_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2024 OpenSSF Scorecard Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package e2e

import (
"context"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

"github.com/ossf/scorecard/v5/internal/packageclient"
)

var _ = Describe("E2E TEST: depsdevclient.GetProjectPackageVersions", func() {
var client packageclient.ProjectPackageClient

Context("E2E TEST: Confirm ProjectPackageClient works", func() {
It("Should receive a non-empty response from deps.dev for existing projects", func() {
client = packageclient.CreateDepsDevClient()
versions, err := client.GetProjectPackageVersions(
context.Background(), "github.com", "ossf/scorecard",
)
Expect(err).Should(BeNil())
Expect(len(versions.Versions)).Should(BeNumerically(">", 0))
})
It("Should error from deps.dev for nonexistent projects", func() {
client = packageclient.CreateDepsDevClient()
versions, err := client.GetProjectPackageVersions(
context.Background(), "github.com", "ossf/scorecard-E2E-TEST-DOES-NOT-EXIST",
)
Expect(err).ShouldNot(BeNil())
Expect(versions).Should(BeNil())
})
It("Should receive a non-empty response from deps.dev for existing projects", func() {
client = packageclient.CreateDepsDevClient()
versions, err := client.GetProjectPackageVersions(
context.Background(), "gitlab.com", "libtiff/libtiff",
)
Expect(err).Should(BeNil())
Expect(len(versions.Versions)).Should(BeNumerically(">", 0))
})
})
})
89 changes: 89 additions & 0 deletions internal/packageclient/depsdev.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2024 OpenSSF Scorecard Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package packageclient

import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)

// This interface lets Scorecard look up package manager metadata for a project.
type ProjectPackageClient interface {
GetProjectPackageVersions(ctx context.Context, host, project string) (*ProjectPackageVersions, error)
}

type depsDevClient struct {
client *http.Client
}

type ProjectPackageVersions struct {
// field alignment
//nolint:govet
Versions []struct {
VersionKey struct {
System string `json:"system"`
Name string `json:"name"`
Version string `json:"version"`
} `json:"versionKey"`
SLSAProvenances []struct {
SourceRepository string `json:"sourceRepository"`
Commit string `json:"commit"`
Verified bool `json:"verified"`
} `json:"slsaProvenances"`
RelationType string `json:"relationType"`
RelationProvenance string `json:"relationProvenance"`
} `json:"versions"`
}

func CreateDepsDevClient() ProjectPackageClient {
return depsDevClient{
client: &http.Client{},
}
}

func (d depsDevClient) GetProjectPackageVersions(
ctx context.Context, host, project string,
) (*ProjectPackageVersions, error) {
path := fmt.Sprintf("%s/%s", host, project)
query := fmt.Sprintf("https://api.deps.dev/v3/projects/%s:packageversions", url.QueryEscape(path))

req, err := http.NewRequestWithContext(ctx, http.MethodGet, query, nil)
if err != nil {
return nil, fmt.Errorf("http.NewRequestWithContext: %w", err)
}

resp, err := d.client.Do(req)
if err != nil {
return nil, fmt.Errorf("deps.dev GetProjectPackageVersions: %w", err)
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("resp.Body.Read: %w", err)
}

var res ProjectPackageVersions
err = json.Unmarshal(body, &res)
if err != nil {
return nil, fmt.Errorf("json.Unmarshal from deps.dev: %w", err)
}

return &res, nil
}

0 comments on commit f842292

Please sign in to comment.