aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeith Zantow <kzantow@gmail.com>2022-10-11 13:28:47 -0400
committerKeith Zantow <kzantow@gmail.com>2022-10-11 13:28:47 -0400
commitd326e1063b8265703fae4677e7fd63eb97e2df28 (patch)
tree8fbcb16d0a5147c7d8d6cff1bfb2c86b0b22b2eb
parent77794935eaa9bc95a1477b73ffb096f0986c1c84 (diff)
downloadspdx-tools-d326e1063b8265703fae4677e7fd63eb97e2df28.tar.gz
chore: update a few missed functions for 2.3
Signed-off-by: Keith Zantow <kzantow@gmail.com>
-rw-r--r--spdxlib/described_elements.go52
-rw-r--r--spdxlib/described_elements_test.go170
-rw-r--r--spdxlib/documents.go31
-rw-r--r--spdxlib/documents_test.go87
-rw-r--r--spdxlib/relationships.go15
5 files changed, 355 insertions, 0 deletions
diff --git a/spdxlib/described_elements.go b/spdxlib/described_elements.go
index 61833b4..a2a6356 100644
--- a/spdxlib/described_elements.go
+++ b/spdxlib/described_elements.go
@@ -9,6 +9,7 @@ import (
"github.com/spdx/tools-golang/spdx/common"
"github.com/spdx/tools-golang/spdx/v2_1"
"github.com/spdx/tools-golang/spdx/v2_2"
+ "github.com/spdx/tools-golang/spdx/v2_3"
)
// GetDescribedPackageIDs2_1 returns a slice of ElementIDs for all Packages
@@ -112,3 +113,54 @@ func GetDescribedPackageIDs2_2(doc *v2_2.Document) ([]common.ElementID, error) {
return eIDs, nil
}
+
+// GetDescribedPackageIDs2_3 returns a slice of ElementIDs for all Packages
+// in this Document that it "describes," according to SPDX rules:
+// - If the document has only one Package, its ID is returned.
+// - If the document has 2+ Packages, it returns the IDs of those that have
+// a DESCRIBES (or DESCRIBED_BY) relationship to this DOCUMENT.
+func GetDescribedPackageIDs2_3(doc *v2_3.Document) ([]common.ElementID, error) {
+ // if nil Packages map or zero packages in it, return empty slice
+ if doc.Packages == nil {
+ return nil, fmt.Errorf("Packages map is nil")
+ }
+ if len(doc.Packages) == 0 {
+ return nil, fmt.Errorf("no Packages in Document")
+ }
+ if len(doc.Packages) == 1 {
+ // get first (only) one and return its ID
+ for _, pkg := range doc.Packages {
+ return []common.ElementID{pkg.PackageSPDXIdentifier}, nil
+ }
+ }
+
+ // two or more packages, so we need to go through the relationships,
+ // find DESCRIBES or DESCRIBED_BY for this DOCUMENT, verify they are
+ // valid IDs in this document's packages, and return them
+ if doc.Relationships == nil {
+ return nil, fmt.Errorf("multiple Packages in Document but Relationships slice is nil")
+ }
+
+ eIDs, err := FilterRelationships2_3(doc, func(relationship *v2_3.Relationship) *common.ElementID {
+ refDocument := common.MakeDocElementID("", "DOCUMENT")
+
+ if relationship.Relationship == "DESCRIBES" && relationship.RefA == refDocument {
+ return &relationship.RefB.ElementRefID
+ } else if relationship.Relationship == "DESCRIBED_BY" && relationship.RefB == refDocument {
+ return &relationship.RefA.ElementRefID
+ }
+
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ if len(eIDs) == 0 {
+ return nil, fmt.Errorf("no DESCRIBES or DESCRIBED_BY relationships found for this Document")
+ }
+
+ eIDs = SortElementIDs(eIDs)
+
+ return eIDs, nil
+}
diff --git a/spdxlib/described_elements_test.go b/spdxlib/described_elements_test.go
index 8ea9cd5..8b7eb10 100644
--- a/spdxlib/described_elements_test.go
+++ b/spdxlib/described_elements_test.go
@@ -8,6 +8,7 @@ import (
"github.com/spdx/tools-golang/spdx/common"
"github.com/spdx/tools-golang/spdx/v2_1"
"github.com/spdx/tools-golang/spdx/v2_2"
+ "github.com/spdx/tools-golang/spdx/v2_3"
)
// ===== 2.1 tests =====
@@ -347,3 +348,172 @@ func Test2_2FailsToGetDescribedPackagesIfNilMap(t *testing.T) {
t.Fatalf("expected non-nil error, got nil")
}
}
+
+// ===== 2.3 tests =====
+
+func Test2_3CanGetIDsOfDescribedPackages(t *testing.T) {
+ // set up document and some packages and relationships
+ doc := &v2_3.Document{
+ SPDXVersion: "SPDX-2.3",
+ DataLicense: "CC0-1.0",
+ SPDXIdentifier: common.ElementID("DOCUMENT"),
+ CreationInfo: &v2_3.CreationInfo{},
+ Packages: []*v2_3.Package{
+ {PackageName: "pkg1", PackageSPDXIdentifier: "p1"},
+ {PackageName: "pkg2", PackageSPDXIdentifier: "p2"},
+ {PackageName: "pkg3", PackageSPDXIdentifier: "p3"},
+ {PackageName: "pkg4", PackageSPDXIdentifier: "p4"},
+ {PackageName: "pkg5", PackageSPDXIdentifier: "p5"},
+ },
+ Relationships: []*v2_3.Relationship{
+ &v2_3.Relationship{
+ RefA: common.MakeDocElementID("", "DOCUMENT"),
+ RefB: common.MakeDocElementID("", "p1"),
+ Relationship: "DESCRIBES",
+ },
+ &v2_3.Relationship{
+ RefA: common.MakeDocElementID("", "DOCUMENT"),
+ RefB: common.MakeDocElementID("", "p5"),
+ Relationship: "DESCRIBES",
+ },
+ // inverse relationship -- should also get detected
+ &v2_3.Relationship{
+ RefA: common.MakeDocElementID("", "p4"),
+ RefB: common.MakeDocElementID("", "DOCUMENT"),
+ Relationship: "DESCRIBED_BY",
+ },
+ // different relationship
+ &v2_3.Relationship{
+ RefA: common.MakeDocElementID("", "p1"),
+ RefB: common.MakeDocElementID("", "p2"),
+ Relationship: "DEPENDS_ON",
+ },
+ },
+ }
+
+ // request IDs for DESCRIBES / DESCRIBED_BY relationships
+ describedPkgIDs, err := GetDescribedPackageIDs2_3(doc)
+ if err != nil {
+ t.Fatalf("expected nil error, got %v", err)
+ }
+ // should be three of the five IDs, returned in alphabetical order
+ if len(describedPkgIDs) != 3 {
+ t.Fatalf("expected %d packages, got %d", 3, len(describedPkgIDs))
+ }
+ if describedPkgIDs[0] != common.ElementID("p1") {
+ t.Errorf("expected %v, got %v", common.ElementID("p1"), describedPkgIDs[0])
+ }
+ if describedPkgIDs[1] != common.ElementID("p4") {
+ t.Errorf("expected %v, got %v", common.ElementID("p4"), describedPkgIDs[1])
+ }
+ if describedPkgIDs[2] != common.ElementID("p5") {
+ t.Errorf("expected %v, got %v", common.ElementID("p5"), describedPkgIDs[2])
+ }
+}
+
+func Test2_3GetDescribedPackagesReturnsSinglePackageIfOnlyOne(t *testing.T) {
+ // set up document and one package, but no relationships
+ // b/c only one package
+ doc := &v2_3.Document{
+ SPDXVersion: "SPDX-2.2",
+ DataLicense: "CC0-1.0",
+ SPDXIdentifier: common.ElementID("DOCUMENT"),
+ CreationInfo: &v2_3.CreationInfo{},
+ Packages: []*v2_3.Package{
+ {PackageName: "pkg1", PackageSPDXIdentifier: "p1"},
+ },
+ }
+
+ // request IDs for DESCRIBES / DESCRIBED_BY relationships
+ describedPkgIDs, err := GetDescribedPackageIDs2_3(doc)
+ if err != nil {
+ t.Fatalf("expected nil error, got %v", err)
+ }
+ // should return the single package
+ if len(describedPkgIDs) != 1 {
+ t.Fatalf("expected %d package, got %d", 1, len(describedPkgIDs))
+ }
+ if describedPkgIDs[0] != common.ElementID("p1") {
+ t.Errorf("expected %v, got %v", common.ElementID("p1"), describedPkgIDs[0])
+ }
+}
+
+func Test2_3FailsToGetDescribedPackagesIfMoreThanOneWithoutDescribesRelationship(t *testing.T) {
+ // set up document and multiple packages, but no DESCRIBES relationships
+ doc := &v2_3.Document{
+ SPDXVersion: "SPDX-2.2",
+ DataLicense: "CC0-1.0",
+ SPDXIdentifier: common.ElementID("DOCUMENT"),
+ CreationInfo: &v2_3.CreationInfo{},
+ Packages: []*v2_3.Package{
+ {PackageName: "pkg1", PackageSPDXIdentifier: "p1"},
+ {PackageName: "pkg2", PackageSPDXIdentifier: "p2"},
+ {PackageName: "pkg3", PackageSPDXIdentifier: "p3"},
+ {PackageName: "pkg4", PackageSPDXIdentifier: "p4"},
+ {PackageName: "pkg5", PackageSPDXIdentifier: "p5"},
+ },
+ Relationships: []*v2_3.Relationship{
+ // different relationship
+ &v2_3.Relationship{
+ RefA: common.MakeDocElementID("", "p1"),
+ RefB: common.MakeDocElementID("", "p2"),
+ Relationship: "DEPENDS_ON",
+ },
+ },
+ }
+
+ _, err := GetDescribedPackageIDs2_3(doc)
+ if err == nil {
+ t.Fatalf("expected non-nil error, got nil")
+ }
+}
+
+func Test2_3FailsToGetDescribedPackagesIfMoreThanOneWithNilRelationships(t *testing.T) {
+ // set up document and multiple packages, but no relationships slice
+ doc := &v2_3.Document{
+ SPDXVersion: "SPDX-2.2",
+ DataLicense: "CC0-1.0",
+ SPDXIdentifier: common.ElementID("DOCUMENT"),
+ CreationInfo: &v2_3.CreationInfo{},
+ Packages: []*v2_3.Package{
+ {PackageName: "pkg1", PackageSPDXIdentifier: "p1"},
+ {PackageName: "pkg2", PackageSPDXIdentifier: "p2"},
+ },
+ }
+
+ _, err := GetDescribedPackageIDs2_3(doc)
+ if err == nil {
+ t.Fatalf("expected non-nil error, got nil")
+ }
+}
+
+func Test2_3FailsToGetDescribedPackagesIfZeroPackagesInMap(t *testing.T) {
+ // set up document but no packages
+ doc := &v2_3.Document{
+ SPDXVersion: "SPDX-2.2",
+ DataLicense: "CC0-1.0",
+ SPDXIdentifier: common.ElementID("DOCUMENT"),
+ CreationInfo: &v2_3.CreationInfo{},
+ Packages: []*v2_3.Package{},
+ }
+
+ _, err := GetDescribedPackageIDs2_3(doc)
+ if err == nil {
+ t.Fatalf("expected non-nil error, got nil")
+ }
+}
+
+func Test2_3FailsToGetDescribedPackagesIfNilMap(t *testing.T) {
+ // set up document but no packages
+ doc := &v2_3.Document{
+ SPDXVersion: "SPDX-2.2",
+ DataLicense: "CC0-1.0",
+ SPDXIdentifier: common.ElementID("DOCUMENT"),
+ CreationInfo: &v2_3.CreationInfo{},
+ }
+
+ _, err := GetDescribedPackageIDs2_3(doc)
+ if err == nil {
+ t.Fatalf("expected non-nil error, got nil")
+ }
+}
diff --git a/spdxlib/documents.go b/spdxlib/documents.go
index 8560f08..dc41803 100644
--- a/spdxlib/documents.go
+++ b/spdxlib/documents.go
@@ -7,6 +7,7 @@ import (
"github.com/spdx/tools-golang/spdx/common"
"github.com/spdx/tools-golang/spdx/v2_1"
"github.com/spdx/tools-golang/spdx/v2_2"
+ "github.com/spdx/tools-golang/spdx/v2_3"
)
// ValidateDocument2_1 returns an error if the Document is found to be invalid, or nil if the Document is valid.
@@ -68,3 +69,33 @@ func ValidateDocument2_2(doc *v2_2.Document) error {
return nil
}
+
+// ValidateDocument2_3 returns an error if the Document is found to be invalid, or nil if the Document is valid.
+// Currently, this only verifies that all Element IDs mentioned in Relationships exist in the Document as either a
+// Package or an UnpackagedFile.
+func ValidateDocument2_3(doc *v2_3.Document) error {
+ // cache a map of package IDs for quick lookups
+ validElementIDs := make(map[common.ElementID]bool)
+ for _, docPackage := range doc.Packages {
+ validElementIDs[docPackage.PackageSPDXIdentifier] = true
+ }
+
+ for _, unpackagedFile := range doc.Files {
+ validElementIDs[unpackagedFile.FileSPDXIdentifier] = true
+ }
+
+ // add the Document element ID
+ validElementIDs[common.MakeDocElementID("", "DOCUMENT").ElementRefID] = true
+
+ for _, relationship := range doc.Relationships {
+ if !validElementIDs[relationship.RefA.ElementRefID] {
+ return fmt.Errorf("%s used in relationship but no such package exists", string(relationship.RefA.ElementRefID))
+ }
+
+ if !validElementIDs[relationship.RefB.ElementRefID] {
+ return fmt.Errorf("%s used in relationship but no such package exists", string(relationship.RefB.ElementRefID))
+ }
+ }
+
+ return nil
+}
diff --git a/spdxlib/documents_test.go b/spdxlib/documents_test.go
index 586cabd..6d04f38 100644
--- a/spdxlib/documents_test.go
+++ b/spdxlib/documents_test.go
@@ -8,6 +8,7 @@ import (
"github.com/spdx/tools-golang/spdx/common"
"github.com/spdx/tools-golang/spdx/v2_1"
"github.com/spdx/tools-golang/spdx/v2_2"
+ "github.com/spdx/tools-golang/spdx/v2_3"
)
// ===== 2.1 tests =====
@@ -181,3 +182,89 @@ func Test2_2InvalidDocumentFailsValidation(t *testing.T) {
t.Fatalf("expected non-nil error, got nil")
}
}
+
+// ===== 2.3 tests =====
+
+func Test2_3ValidDocumentPassesValidation(t *testing.T) {
+ // set up document and some packages and relationships
+ doc := &v2_3.Document{
+ SPDXVersion: "SPDX-2.3",
+ DataLicense: "CC0-1.0",
+ SPDXIdentifier: common.ElementID("DOCUMENT"),
+ CreationInfo: &v2_3.CreationInfo{},
+ Packages: []*v2_3.Package{
+ {PackageName: "pkg1", PackageSPDXIdentifier: "p1"},
+ {PackageName: "pkg2", PackageSPDXIdentifier: "p2"},
+ {PackageName: "pkg3", PackageSPDXIdentifier: "p3"},
+ {PackageName: "pkg4", PackageSPDXIdentifier: "p4"},
+ {PackageName: "pkg5", PackageSPDXIdentifier: "p5"},
+ },
+ Relationships: []*v2_3.Relationship{
+ {
+ RefA: common.MakeDocElementID("", "DOCUMENT"),
+ RefB: common.MakeDocElementID("", "p1"),
+ Relationship: "DESCRIBES",
+ },
+ {
+ RefA: common.MakeDocElementID("", "DOCUMENT"),
+ RefB: common.MakeDocElementID("", "p5"),
+ Relationship: "DESCRIBES",
+ },
+ // inverse relationship -- should also get detected
+ {
+ RefA: common.MakeDocElementID("", "p4"),
+ RefB: common.MakeDocElementID("", "DOCUMENT"),
+ Relationship: "DESCRIBED_BY",
+ },
+ // different relationship
+ {
+ RefA: common.MakeDocElementID("", "p1"),
+ RefB: common.MakeDocElementID("", "p2"),
+ Relationship: "DEPENDS_ON",
+ },
+ },
+ }
+
+ err := ValidateDocument2_3(doc)
+ if err != nil {
+ t.Fatalf("expected nil error, got: %s", err.Error())
+ }
+}
+
+func Test2_3InvalidDocumentFailsValidation(t *testing.T) {
+ // set up document and some packages and relationships
+ doc := &v2_3.Document{
+ SPDXVersion: "SPDX-2.1",
+ DataLicense: "CC0-1.0",
+ SPDXIdentifier: common.ElementID("DOCUMENT"),
+ CreationInfo: &v2_3.CreationInfo{},
+ Packages: []*v2_3.Package{
+ {PackageName: "pkg1", PackageSPDXIdentifier: "p1"},
+ {PackageName: "pkg2", PackageSPDXIdentifier: "p2"},
+ {PackageName: "pkg3", PackageSPDXIdentifier: "p3"},
+ },
+ Relationships: []*v2_3.Relationship{
+ {
+ RefA: common.MakeDocElementID("", "DOCUMENT"),
+ RefB: common.MakeDocElementID("", "p1"),
+ Relationship: "DESCRIBES",
+ },
+ {
+ RefA: common.MakeDocElementID("", "DOCUMENT"),
+ RefB: common.MakeDocElementID("", "p5"),
+ Relationship: "DESCRIBES",
+ },
+ // invalid ID p99
+ {
+ RefA: common.MakeDocElementID("", "p1"),
+ RefB: common.MakeDocElementID("", "p99"),
+ Relationship: "DEPENDS_ON",
+ },
+ },
+ }
+
+ err := ValidateDocument2_3(doc)
+ if err == nil {
+ t.Fatalf("expected non-nil error, got nil")
+ }
+}
diff --git a/spdxlib/relationships.go b/spdxlib/relationships.go
index 5ff4197..aa807d0 100644
--- a/spdxlib/relationships.go
+++ b/spdxlib/relationships.go
@@ -6,6 +6,7 @@ import (
"github.com/spdx/tools-golang/spdx/common"
"github.com/spdx/tools-golang/spdx/v2_1"
"github.com/spdx/tools-golang/spdx/v2_2"
+ "github.com/spdx/tools-golang/spdx/v2_3"
)
// FilterRelationships2_1 returns a slice of Element IDs returned by the given filter closure. The closure is passed
@@ -35,3 +36,17 @@ func FilterRelationships2_2(doc *v2_2.Document, filter func(*v2_2.Relationship)
return elementIDs, nil
}
+
+// FilterRelationships2_3 returns a slice of Element IDs returned by the given filter closure. The closure is passed
+// one relationship at a time, and it can return an ElementID or nil.
+func FilterRelationships2_3(doc *v2_3.Document, filter func(*v2_3.Relationship) *common.ElementID) ([]common.ElementID, error) {
+ elementIDs := []common.ElementID{}
+
+ for _, relationship := range doc.Relationships {
+ if id := filter(relationship); id != nil {
+ elementIDs = append(elementIDs, *id)
+ }
+ }
+
+ return elementIDs, nil
+}