This repository has been archived by the owner on Dec 10, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 961
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add dependency list export client service (#2063)
* feat: add dependency list client service * add missing line endings * rename client service to match endpoint name * fix and refactor * Fix casing in comment to match struct name * fix default export type and make DownloadDependencyListExport return an io.Reader * fix inconsistent indentation in commented code example * Update dependency_list_export.go --------- Co-authored-by: Timo Furrer <[email protected]>
- Loading branch information
1 parent
912f9bf
commit 8186bd9
Showing
7 changed files
with
253 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package gitlab | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
) | ||
|
||
type DependencyListExportService struct { | ||
client *Client | ||
} | ||
|
||
// CreateDependencyListExportOptions represents the available CreateDependencyListExport() | ||
// options. | ||
// | ||
// GitLab API docs: | ||
// https://docs.gitlab.com/ee/api/dependency_list_export.html#create-a-pipeline-level-dependency-list-export | ||
type CreateDependencyListExportOptions struct { | ||
ExportType *string `url:"export_type" json:"export_type"` | ||
} | ||
|
||
// DependencyListExport represents a request for a GitLab project's dependency list. | ||
// | ||
// GitLab API docs: | ||
// https://docs.gitlab.com/ee/api/dependency_list_export.html#create-a-pipeline-level-dependency-list-export | ||
type DependencyListExport struct { | ||
ID int `json:"id"` | ||
HasFinished bool `json:"has_finished"` | ||
Self string `json:"self"` | ||
Download string `json:"download"` | ||
} | ||
|
||
const defaultExportType = "sbom" | ||
|
||
// CreateDependencyListExport creates a new CycloneDX JSON export for all the project dependencies | ||
// detected in a pipeline. | ||
// | ||
// If an authenticated user does not have permission to read_dependency, this request returns a 403 | ||
// Forbidden status code. | ||
// | ||
// SBOM exports can be only accessed by the export’s author. | ||
// | ||
// GitLab docs: | ||
// https://docs.gitlab.com/ee/api/dependency_list_export.html#create-a-pipeline-level-dependency-list-export | ||
func (s *DependencyListExportService) CreateDependencyListExport(pipelineID int, opt *CreateDependencyListExportOptions, options ...RequestOptionFunc) (*DependencyListExport, *Response, error) { | ||
// POST /pipelines/:id/dependency_list_exports | ||
createExportPath := fmt.Sprintf("pipelines/%d/dependency_list_exports", pipelineID) | ||
|
||
if opt == nil { | ||
opt = &CreateDependencyListExportOptions{} | ||
} | ||
if opt.ExportType == nil { | ||
opt.ExportType = Ptr(defaultExportType) | ||
} | ||
|
||
req, err := s.client.NewRequest(http.MethodPost, createExportPath, opt, options) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
export := new(DependencyListExport) | ||
resp, err := s.client.Do(req, &export) | ||
if err != nil { | ||
return nil, resp, err | ||
} | ||
|
||
return export, resp, nil | ||
} | ||
|
||
// GetDependencyListExport gets metadata about a single dependency list export. | ||
// | ||
// GitLab docs: | ||
// https://docs.gitlab.com/ee/api/dependency_list_export.html#get-single-dependency-list-export | ||
func (s *DependencyListExportService) GetDependencyListExport(id int, options ...RequestOptionFunc) (*DependencyListExport, *Response, error) { | ||
// GET /dependency_list_exports/:id | ||
getExportPath := fmt.Sprintf("dependency_list_exports/%d", id) | ||
|
||
req, err := s.client.NewRequest(http.MethodGet, getExportPath, nil, options) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
export := new(DependencyListExport) | ||
resp, err := s.client.Do(req, &export) | ||
if err != nil { | ||
return nil, resp, err | ||
} | ||
|
||
return export, resp, nil | ||
} | ||
|
||
// DownloadDependencyListExport downloads a single dependency list export. | ||
// | ||
// The github.com/CycloneDX/cyclonedx-go package can be used to parse the data from the returned io.Reader. | ||
// | ||
// sbom := new(cdx.BOM) | ||
// decoder := cdx.NewBOMDecoder(reader, cdx.BOMFileFormatJSON) | ||
// | ||
// if err = decoder.Decode(sbom); err != nil { | ||
// panic(err) | ||
// } | ||
// | ||
// GitLab docs: | ||
// https://docs.gitlab.com/ee/api/dependency_list_export.html#download-dependency-list-export | ||
func (s *DependencyListExportService) DownloadDependencyListExport(id int, options ...RequestOptionFunc) (io.Reader, *Response, error) { | ||
// GET /dependency_list_exports/:id/download | ||
downloadExportPath := fmt.Sprintf("dependency_list_exports/%d/download", id) | ||
|
||
req, err := s.client.NewRequest(http.MethodGet, downloadExportPath, nil, options) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
var sbomBuffer bytes.Buffer | ||
resp, err := s.client.Do(req, &sbomBuffer) | ||
if err != nil { | ||
return nil, resp, err | ||
} | ||
|
||
return &sbomBuffer, resp, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package gitlab | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"io" | ||
"net/http" | ||
"os" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestCreateDependencyListExport(t *testing.T) { | ||
mux, client := setup(t) | ||
|
||
mux.HandleFunc("/api/v4/pipelines/1234/dependency_list_exports", func(w http.ResponseWriter, r *http.Request) { | ||
testMethod(t, r, http.MethodPost) | ||
body, err := io.ReadAll(r.Body) | ||
require.NoError(t, err) | ||
|
||
var content CreateDependencyListExportOptions | ||
err = json.Unmarshal(body, &content) | ||
require.NoError(t, err) | ||
|
||
assert.Equal(t, "sbom", *content.ExportType) | ||
mustWriteHTTPResponse(t, w, "testdata/create_dependency_list_export.json") | ||
}) | ||
|
||
d := &CreateDependencyListExportOptions{ | ||
ExportType: Ptr("sbom"), | ||
} | ||
|
||
export, _, err := client.DependencyListExport.CreateDependencyListExport(1234, d) | ||
require.NoError(t, err) | ||
|
||
want := &DependencyListExport{ | ||
ID: 5678, | ||
HasFinished: false, | ||
Self: "http://gitlab.example.com/api/v4/dependency_list_exports/5678", | ||
Download: "http://gitlab.example.com/api/v4/dependency_list_exports/5678/download", | ||
} | ||
require.Equal(t, want, export) | ||
} | ||
|
||
func TestGetDependencyListExport(t *testing.T) { | ||
mux, client := setup(t) | ||
|
||
mux.HandleFunc("/api/v4/dependency_list_exports/5678", func(w http.ResponseWriter, r *http.Request) { | ||
testMethod(t, r, http.MethodGet) | ||
mustWriteHTTPResponse(t, w, "testdata/get_dependency_list_export.json") | ||
}) | ||
|
||
export, _, err := client.DependencyListExport.GetDependencyListExport(5678) | ||
require.NoError(t, err) | ||
|
||
want := &DependencyListExport{ | ||
ID: 5678, | ||
HasFinished: true, | ||
Self: "http://gitlab.example.com/api/v4/dependency_list_exports/5678", | ||
Download: "http://gitlab.example.com/api/v4/dependency_list_exports/5678/download", | ||
} | ||
require.Equal(t, want, export) | ||
} | ||
|
||
func TestDownloadDependencyListExport(t *testing.T) { | ||
mux, client := setup(t) | ||
|
||
mux.HandleFunc("/api/v4/dependency_list_exports/5678/download", func(w http.ResponseWriter, r *http.Request) { | ||
testMethod(t, r, http.MethodGet) | ||
mustWriteHTTPResponse(t, w, "testdata/download_dependency_list_export.json") | ||
}) | ||
|
||
sbomReader, _, err := client.DependencyListExport.DownloadDependencyListExport(5678) | ||
require.NoError(t, err) | ||
|
||
expectedSbom, err := os.ReadFile("testdata/download_dependency_list_export.json") | ||
require.NoError(t, err) | ||
|
||
var want bytes.Buffer | ||
want.Write(expectedSbom) | ||
|
||
require.Equal(t, &want, sbomReader) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"id": 5678, | ||
"has_finished": false, | ||
"self": "http://gitlab.example.com/api/v4/dependency_list_exports/5678", | ||
"download": "http://gitlab.example.com/api/v4/dependency_list_exports/5678/download" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
{ | ||
"bomFormat": "CycloneDX", | ||
"specVersion": "1.4", | ||
"serialNumber": "urn:uuid:3fa3b1c2-7e21-4dae-917b-b320f6d25ae1", | ||
"version": 1, | ||
"metadata": { | ||
"timestamp": "2024-11-14T23:39:16.117Z", | ||
"authors": [{ "name": "GitLab", "email": "[email protected]" }], | ||
"properties": [ | ||
{ | ||
"name": "gitlab:dependency_scanning:input_file:path", | ||
"value": "my_package_manager.lock" | ||
}, | ||
{ | ||
"name": "gitlab:dependency_scanning:package_manager:name", | ||
"value": "my_package_manager" | ||
}, | ||
{ "name": "gitlab:meta:schema_version", "value": "1" } | ||
], | ||
"tools": [{ "vendor": "GitLab", "name": "Gemnasium", "version": "5.8.0" }] | ||
}, | ||
"components": [ | ||
{ | ||
"name": "dummy", | ||
"version": "1.0.0", | ||
"purl": "pkg:testing/[email protected]", | ||
"type": "library", | ||
"licenses": [{ "license": { "name": "unknown" } }] | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"id": 5678, | ||
"has_finished": true, | ||
"self": "http://gitlab.example.com/api/v4/dependency_list_exports/5678", | ||
"download": "http://gitlab.example.com/api/v4/dependency_list_exports/5678/download" | ||
} |